

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/IKKIS_LOGO.png">
  <link rel="icon" href="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/IKKIS_LOGO.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="author" content="Ikki">
  <meta name="keywords" content="">
  
    <meta name="description" content="React&#96;官方提供了很多&#96;Api&#96;，只是这些&#96;Api&#96;我们并不常用，所以往往会忽略它们，但在一些特定的场景下，这些&#96;Api&#96;也会起到关键性的作用，所以今天就逐个盘点一下，说说它们的使用方法和使用场景。">
<meta property="og:type" content="article">
<meta property="og:title" content="react-api">
<meta property="og:url" content="http://81.68.132.127/2022/10/17/react/basic/react-all-api/index.html">
<meta property="og:site_name" content="IKKI">
<meta property="og:description" content="React&#96;官方提供了很多&#96;Api&#96;，只是这些&#96;Api&#96;我们并不常用，所以往往会忽略它们，但在一些特定的场景下，这些&#96;Api&#96;也会起到关键性的作用，所以今天就逐个盘点一下，说说它们的使用方法和使用场景。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/beijing/iTab-47ve2o.jpeg">
<meta property="article:published_time" content="2022-10-17T11:08:28.000Z">
<meta property="article:modified_time" content="2022-10-30T12:33:52.994Z">
<meta property="article:author" content="Ikki">
<meta property="article:tag" content="react">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/beijing/iTab-47ve2o.jpeg">
  
  
    <meta name="referrer" content="no-referrer-when-downgrade">
  
  
  <title>react-api - IKKI</title>

  <link  rel="stylesheet" href="https://lib.baomitu.com/twitter-bootstrap/4.6.1/css/bootstrap.min.css" />



  <link  rel="stylesheet" href="https://lib.baomitu.com/github-markdown-css/4.0.0/github-markdown.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/hint.css/2.7.0/hint.min.css" />

  <link  rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css" />



<!-- 主题依赖的图标库，不要自行修改 -->
<!-- Do not modify the link that theme dependent icons -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_hj8rtnfg7um.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_lbnruvf0jn.css">


<link  rel="stylesheet" href="/css/main.css" />


  <link id="highlight-css" rel="stylesheet" href="/css/highlight.css" />
  
    <link id="highlight-css-dark" rel="stylesheet" href="/css/highlight-dark.css" />
  



  
<link rel="stylesheet" href="/fluid/css/fluid-extention.css">
<link rel="stylesheet" href="/fluid/css/nihao.css">



  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    Fluid.ctx = Object.assign({}, Fluid.ctx)
    var CONFIG = {"hostname":"81.68.132.127","root":"/","version":"1.9.3","typing":{"enable":true,"typeSpeed":70,"cursorChar":"_","loop":false,"scope":[]},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"left","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"code_language":{"enable":true,"default":"TEXT"},"copy_btn":true,"image_caption":{"enable":true},"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"placement":"left","headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":3},"lazyload":{"enable":true,"loading_img":"https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":false,"follow_dnt":true,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname","ignore_local":false}},"search_path":"/local-search.xml"};

    if (CONFIG.web_analytics.follow_dnt) {
      var dntVal = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
      Fluid.ctx.dnt = dntVal && (dntVal.startsWith('1') || dntVal.startsWith('yes') || dntVal.startsWith('on'));
    }
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
  


  
<meta name="generator" content="Hexo 5.4.2"></head>


<body>
  

  <header>
    

<div class="header-inner" style="height: 100vh;">
  <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/">
      <strong>Ikki-BUG制造者</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              &nbsp;<i class="iconfont icon-search"></i>&nbsp;
            </a>
          </li>
          
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

  

<div id="banner" class="banner" parallax=true
     style="background: url('http://image.ikki0603.cn') no-repeat center center; background-size: cover;">
  <div class="full-bg-img">
    <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
      <div class="banner-text text-center fade-in-up">
        <div class="h2">
          
            <span id="subtitle" data-typed-text="react-api"></span>
          
        </div>

        
          
  <div class="mt-3">
    
    
      <span class="post-meta">
        <i class="iconfont icon-date-fill" aria-hidden="true"></i>
        <time datetime="2022-10-17 19:08" pubdate>
          2022年10月17日 晚上
        </time>
      </span>
    
  </div>

  <div class="mt-1">
    
      <span class="post-meta mr-2">
        <i class="iconfont icon-chart"></i>
        
          45k 字
        
      </span>
    

    
      <span class="post-meta mr-2">
        <i class="iconfont icon-clock-fill"></i>
        
        
        
          372 分钟
        
      </span>
    

    
    
      
        <span id="busuanzi_container_page_pv" style="display: none">
          <i class="iconfont icon-eye" aria-hidden="true"></i>
          <span id="busuanzi_value_page_pv"></span> 次
        </span>
        
      
    
  </div>


        
      </div>

      
        <div class="scroll-down-bar">
          <i class="iconfont icon-arrowdown"></i>
        </div>
      
    </div>
  </div>
</div>

</div>

  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="side-col d-none d-lg-block col-lg-2">
      
  <aside class="sidebar" style="padding-left: 2rem; margin-right: -1rem">
    <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>



  </aside>


    </div>

    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">react-api</h1>
            
              <p class="note note-info">
                
                  
                    本文最后更新于：2022年10月30日 晚上
                  
                
              </p>
            
            
              <div class="markdown-body">
                
                <p>其实<code>React</code>官方提供了很多<code>Api</code>，只是这些<code>Api</code>我们并不常用，所以往往会忽略它们，但在一些特定的场景下，这些<code>Api</code>也会起到关键性的作用，所以今天就逐个盘点一下，说说它们的使用方法和使用场景。</p>
<p>当然这些<code>Api</code>并不需要全部掌握，只需要知道有这个知识点就好了~</p>
<p>本文将会全面总结所有的<code>React</code>Api，包含<code>组件类</code>、<code>工具类</code>、<code>生命周期</code>、<code>react-hooks</code>、<code>react-dom</code>五大模块，并配带示例，帮助大家更好的掌握，大家可以边嗑瓜子边阅读，如有不全、不对的地方欢迎在评论区指出～</p>
<p>由于本文过长，建议<code>点赞</code> +<code>收藏</code>， 在正式开始前，我抽取了一些问题，一起来看看：</p>
<ul>
<li>  1.<code>React v18</code>中对<code>react-dom</code>做了那些改动，增加了那些新的<code>hooks</code>?</li>
<li>  2.<code>useRef</code>除了获取元素的节点信息，还能做什么？</li>
<li>  3.为什么会有<code>Children.map</code>?它与不同的遍历有和不同</li>
<li>  4.类组件的生命周期在不同的版本是怎样变化的？</li>
<li>  5.子元素如何渲染到父元素上面的？</li>
<li>  …</li>
</ul>
<p>其实问题很多，看完这篇文章后，相信一定能帮你解答的非常清楚，还请各位小伙伴多多支持一下</p>
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><blockquote>
<p>在之前的几篇文章中，有自定义<code>hooks</code>、<code>HOC</code>、<code>虚拟DOM</code>和<code>diff算法</code>，多多少少都有React官方Api做为基础条件，我的这个专栏的目的就是对<code>React</code>的深入，就是希望对<code>React</code>有一个全面的提升</p>
<p>写这篇文章的主要目的有：</p>
<ul>
<li>  提升知识广度，要想深入<code>React</code>就必须全面了解<code>React</code>，首先要学会用，要知道，如果连知道都不知道，谈何深入？</li>
<li>  <code>React v18</code> 对 <code>react-dom</code>的改动还是比较大的，并且新增了五个<code>hooks</code>,逐一盘点一下，看看做了那些改动</li>
<li>  这个专栏实际上是循序渐进的，相互之间都有一定的关联，同时要想看懂，也需要有一定的<code>React</code>基础，对刚开始学习<code>React</code>的小伙伴可能并不是太友好，所以特地总结一番，用最简单的示例，帮你掌握这些Api</li>
<li>  对之后的源码有帮助，所以本篇文章将会全面解读<code>React Api</code>的使用方法，和场景，如有不足，还希望在评论区指出～</li>
</ul>
</blockquote>
<p>附上一张今天的学习图谱～</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/562c26a1f2394bd1a27c38cba266b848~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="全面解读ReactApi.png"></p>
<h1 id="1-组件类"><a href="#1-组件类" class="headerlink" title="1 组件类"></a>1 组件类</h1><h2 id="1-1-Component"><a href="#1-1-Component" class="headerlink" title="1.1 Component"></a>1.1 Component</h2><hr>
<p>在React中提供两种形式，一种是<code>类组件</code>，另一种是<code>函数式组件</code>，而在<code>类组件</code>组件中需要使用<code>Component</code>继承，这个组件没有什么好讲的，我们可以看看源码：</p>
<p>文件位置<code>packages/react/src/ReactBaseClasses.js</code></p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/11cdee40208f4dacb18b982fbc492c18~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>可以看出<code>Component</code>进行一些初始化的工作，<code>updater</code>保存着更新组件的方法</p>
<h2 id="1-2-PureComponent"><a href="#1-2-PureComponent" class="headerlink" title="1.2 PureComponent"></a>1.2 PureComponent</h2><p><strong>PureComponent</strong>：会对<code>props</code>和<code>state</code>进行浅比较，跳过不必要的更新，提高组件性能。</p>
<p>可以说<code>PureComponent</code>和<code>Component</code>基本完全一致，但<code>PureComponent</code>会浅比较，也就是较少<code>render</code>渲染的次数，所以<code>PureComponent</code>一般用于性能优化</p>
<p>那么什么是浅比较，举个🌰：</p>
<pre><code class="hljs">import &#123; PureComponent &#125; from &#39;react&#39;;
import &#123; Button &#125; from &#39;antd-mobile&#39;;

class Index extends PureComponent&#123;

  constructor(props)&#123;
      super(props)
      this.state=&#123;
         data:&#123;
            number:0
         &#125;
      &#125;
  &#125;

  render()&#123;
      const &#123; data &#125; = this.state
      return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
          &lt;div&gt; 数字： &#123; data.number  &#125;&lt;/div&gt;
          &lt;Button
            color=&quot;primary&quot; 
            onClick=&#123;() =&gt; &#123;
                const &#123; data &#125; = this.state
                data.number++
                this.setState(&#123; data &#125;)
            &#125;&#125;
          &gt;数字加1&lt;/Button&gt;
      &lt;/div&gt;
  &#125;
&#125;

export default Index;
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9fed4f0cbef7459c8c50069b907e11ab~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img1.gif"></p>
<p>可以发现，当我们点击按钮的时候，数字并没有刷新，这是因为<code>PureComponent</code>会比较两次的<code>data</code>对象，它会认为这种写法并没有改变原先的<code>data</code>,所以不会改变</p>
<p>我们只需要：</p>
<pre><code class="hljs">this.setState(&#123; data: &#123;...data&#125; &#125;)
复制代码
</code></pre>
<p>这样就可以解决这个问题了</p>
<h3 id="1-2-1-与shouldComponentUpdate的关系如何"><a href="#1-2-1-与shouldComponentUpdate的关系如何" class="headerlink" title="1.2.1 与shouldComponentUpdate的关系如何"></a>1.2.1 与shouldComponentUpdate的关系如何</h3><p>在生命周期中有一个<code>shouldComponentUpdate()</code>函数，那么它能改变<code>PureComponent</code>吗？</p>
<p>其实是可以的，<code>shouldComponentUpdate()</code>如果被定义，就会对新旧 <code>props</code>、<code>state</code> 进行 <code>shallowEqual</code> 比较，新旧一旦不一致，便会触发 <code>update</code>。</p>
<p>也可以这么理解：<code>PureComponent</code>通过自带的<code>props</code>和<code>state</code>的浅比较实现了<code>shouldComponentUpdate()</code>，这点<code>Component</code>并不具备</p>
<p><code>PureComponent</code>可能会因深层的数据不一致而产生错误的否定判断，从而导致<code>shouldComponentUpdate</code>结果返回false，界面得不到更新，要谨慎使用</p>
<h2 id="1-3-memo"><a href="#1-3-memo" class="headerlink" title="1.3 memo"></a>1.3 memo</h2><p><strong>memo</strong>：结合了<code>pureComponent纯组件</code>和 <code>componentShouldUpdate</code>功能，会对传入的props进行一次对比，然后根据第二个函数返回值来进一步判断哪些props需要更新</p>
<p>要注意<code>memo</code>是一个<strong>高阶组件</strong>，<code>函数式组件</code>和<code>类组件</code>都可以使用。</p>
<p><code>memo</code>接收两个参数:</p>
<ul>
<li>  第一个参数：组件本身，也就是要优化的组件</li>
<li>  第二个参数：(pre, next) =&gt; boolean, <code>pre</code>：之前的数据， <code>next</code>：现在的数据，返回一个<code>布尔值</code>，若为 <code>true</code>则不更新，为<code>false</code>更新</li>
</ul>
<h3 id="1-3-1-性能优化"><a href="#1-3-1-性能优化" class="headerlink" title="1.3.1 性能优化"></a>1.3.1 性能优化</h3><p>接下来，我们先看这样一个🌰：</p>
<pre><code class="hljs">import React, &#123; Component &#125; from &#39;react&#39;;
import &#123; Button &#125; from &#39;antd-mobile&#39;;

const Child = () =&gt; &#123;
    return &lt;div&gt;
        &#123;console.log(&#39;子组件渲染&#39;)&#125;
        大家好，我是小杜杜～
    &lt;/div&gt;
&#125;

class Index extends Component&#123;

  constructor(props)&#123;
      super(props)
      this.state=&#123;
        flag: true
      &#125;
  &#125;

  render()&#123;
      const &#123; flag &#125; = this.state
      return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
          &lt;Child/&gt;
          &lt;Button
            color=&quot;primary&quot; 
            onClick=&#123;() =&gt; this.setState(&#123; flag: !flag &#125;)&#125;
          &gt;状态切换&#123;JSON.stringify(flag)&#125;&lt;/Button&gt;
      &lt;/div&gt;
  &#125;
&#125;

export default Index;
复制代码
</code></pre>
<p>在上述代码中，我们设置一个子组件，也就是<code>Child</code>和一个按钮，按钮的效果是切换 <code>flag</code> 的状态，可以看出<code>flag</code>和<code>Child</code>之间没有任何关系，那么在切换状态的时候，<code>Child</code>会刷新吗？</p>
<p>直接看看效果：</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/42bb0a9e2a75477782f80d4b93cc4e50~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img2.gif"></p>
<p>可以看出，在我们切换状态的时候，<code>Child</code>实际上也会刷新，我们肯定不希望组件做无关的刷新，那么我们加上<code>memo</code>来看看的效果：</p>
<pre><code class="hljs">const HOCChild = memo(Child, (pre, next) =&gt; &#123;
    return true
&#125;)
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/429335cd0dd842028c19d65b8d147f46~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img3.gif"> 可以看出，加上<code>memo</code>后，<code>Child</code>不会再做无关的渲染，从而达到<code>性能优化</code>的作用</p>
<h3 id="1-3-2-第二个参数的作用"><a href="#1-3-2-第二个参数的作用" class="headerlink" title="1.3.2 第二个参数的作用"></a>1.3.2 第二个参数的作用</h3><p>栗子🌰：</p>
<pre><code class="hljs">import React, &#123; Component, memo &#125; from &#39;react&#39;;
import &#123; Button &#125; from &#39;antd-mobile&#39;;


const Child = (&#123; number &#125;) =&gt; &#123;
    return &lt;div&gt;
        &#123;console.log(&#39;子组件渲染&#39;)&#125;
        大家好，我是小杜杜～
        &lt;p&gt;传递的数字：&#123;number&#125;&lt;/p&gt;
    &lt;/div&gt;
&#125;

const HOCChild = memo(Child, (pre, next) =&gt; &#123;
    if(pre.number === next.number) return true
    if(next.number &lt; 7) return false
    return true
&#125;)

class Index extends Component&#123;

  constructor(props)&#123;
      super(props)
      this.state=&#123;
        flag: true,
        number: 1
      &#125;
  &#125;

  render()&#123;
      const &#123; flag, number &#125; = this.state
      return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
          &lt;HOCChild number=&#123;number&#125; /&gt;
          &lt;Button
            color=&quot;primary&quot; 
            onClick=&#123;() =&gt; this.setState(&#123; flag: !flag&#125;)&#125;
          &gt;状态切换&#123;JSON.stringify(flag)&#125;&lt;/Button&gt;
        &lt;Button
            color=&quot;primary&quot;
            style=&#123;&#123;marginLeft: 8&#125;&#125; 
            onClick=&#123;() =&gt; this.setState(&#123; number: number + 1&#125;)&#125;
        &gt;数字加一：&#123;number&#125;&lt;/Button&gt;
      &lt;/div&gt;
  &#125;
&#125;

export default Index;
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/414605cc3658444d9a36938275ee7b00~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img4.gif"></p>
<p>当数字小于7，才会出发<code>Child</code>的更新，通过返回的布尔值来控制</p>
<h3 id="1-3-3-memo的注意事项"><a href="#1-3-3-memo的注意事项" class="headerlink" title="1.3.3 memo的注意事项"></a>1.3.3 memo的注意事项</h3><p><code>React.memo</code>与<code>PureComponent</code>的区别：</p>
<ul>
<li>  服务对象不同：<code>PureComponent</code> 服务与类组件，<code>React.memo</code>既可以服务于类组件，也可以服务与函数式组件，<code>useMemo</code>服务于函数式组件（后续讲到）</li>
<li>  针对的对象不同：<code>PureComponent</code>针对的是<code>props</code>和<code>state</code>，<code>React.memo</code>只能针对<code>props</code>来决定是否渲染</li>
</ul>
<p>这里还有个小的注意点：<code>memo</code>的第二个参数的返回值与<code>shouldComponentUpdate</code>的返回值是相反的，经常会弄混，还要多多注意</p>
<ul>
<li>  <code>memo</code>:返回 <code>true</code> 组件不渲染 ， 返回 <code>false</code> 组件重新渲染。</li>
<li>  <code>shouldComponentUpdate</code>: 返回 <code>true</code> 组件渲染 ， 返回 <code>false</code> 组件不渲染。</li>
</ul>
<h2 id="1-4-forwardRef"><a href="#1-4-forwardRef" class="headerlink" title="1.4 forwardRef"></a>1.4 forwardRef</h2><p><code>forwardRef</code>：引用传递，是一种通过组件向子组件自动传递引用<code>ref</code>的技术。对于应用者的大多数组件来说没什么作用，但对于一些重复使用的组件，可能有用。</p>
<p>听完介绍是不是感觉云里雾里的，官方对<code>forwardRef</code>的介绍也很少，我们来看看转发的问题</p>
<p>在<code>React</code>中，<code>React</code>不允许<code>ref</code>通过<code>props</code>传递，因为<code>ref</code>是组件中固定存在的，在组件调和的过程中，会被特殊处理，而<code>forwardRef</code>就是为了解决这件事而诞生的，让<code>ref</code>可以通过<code>props</code>传递</p>
<p>举个栗子🌰：父组件想要获取孙组件上的信息，我们直接用<code>ref</code>传递会怎样：</p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1102af2c9a5a4d228fad4c8b645a9995~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="企业微信截图_0bc3e73d-e6b4-45a9-bd66-c43cd8bbea11.png"></p>
<p>接下来看看利用<code>forwardRef</code>来转发下<code>ref</code>，就可以解决这个问题了：</p>
<pre><code class="hljs">import React, &#123; Component, forwardRef &#125; from &#39;react&#39;;

const Son = (&#123;sonRef&#125;) =&gt; &#123;
    return &lt;div&gt;
        &lt;p&gt;孙组件&lt;/p&gt;
        &lt;p ref=&#123;sonRef&#125;&gt;大家好，我是小杜杜～&lt;/p&gt;
    &lt;/div&gt;
&#125;

const Child = (&#123; childRef &#125;) =&gt; &#123;
    return &lt;div&gt;
       &lt;div&gt;子组件&lt;/div&gt;
        &lt;Son sonRef=&#123;childRef&#125; /&gt;
    &lt;/div&gt;
&#125;

const ForwardChild = forwardRef((props, ref) =&gt; &lt;Child childRef=&#123;ref&#125; &#123;...props&#125; /&gt;)

class Index extends Component&#123;

  constructor(props)&#123;
    super(props)
  &#125;
  node = null

  componentDidMount()&#123;
      console.log(this.node)
  &#125;

  render()&#123;
    return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
        &lt;div&gt;父组件&lt;/div&gt;
        &lt;ForwardChild ref=&#123;(node) =&gt; this.node = node&#125; /&gt;
    &lt;/div&gt;
  &#125;
&#125;

export default Index;
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6d986647ea24437a9596f70da0f415cb~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="企业微信截图_c7a4aa75-362d-4bba-8c83-ded0b4dce01b.png"></p>
<p>如此以来就解决了不能在<code>react</code>组件中传递<code>ref</code>的问题，至于复用的组件可能会用到，目前也没思路用<code>forwardRef</code>干嘛，就当熟悉吧～</p>
<h2 id="1-5-Fragment"><a href="#1-5-Fragment" class="headerlink" title="1.5 Fragment"></a>1.5 Fragment</h2><p>在<code>React</code>中，组件是不允许返回多个节点的，如：</p>
<pre><code class="hljs">    return &lt;p&gt;我是小杜杜&lt;/p&gt;
           &lt;p&gt;React&lt;/p&gt;
           &lt;p&gt;Vue&lt;/p&gt;
复制代码
</code></pre>
<p>我们想要解决这种情况需要给为此套一个容器元素，如<code>&lt;div&gt;&lt;/div&gt;</code></p>
<pre><code class="hljs">    return &lt;div&gt;
       &lt;p&gt;我是小杜杜&lt;/p&gt;
       &lt;p&gt;React&lt;/p&gt;
       &lt;p&gt;Vue&lt;/p&gt;
    &lt;/div&gt;
复制代码
</code></pre>
<p>但这样做，无疑会多增加一个节点，所以在<code>16.0</code>后，官方推出了<code>Fragment</code>碎片概念，能够让一个组件返回多个元素,<strong>React.Fragment 等价于<code>&lt;&gt;&lt;/&gt;</code></strong></p>
<pre><code class="hljs">    return &lt;React.Fragment&gt; 
       &lt;p&gt;我是小杜杜&lt;/p&gt;
       &lt;p&gt;React&lt;/p&gt;
       &lt;p&gt;Vue&lt;/p&gt;
    &lt;/React.Fragment&gt;
复制代码
</code></pre>
<p>可以看到<code>React.Fragment</code>实际上是没有节点的  <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a99ffdf9c8c849c0b1d112e8e59d14dd~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="企业微信截图_34d1a03b-acdd-4ed0-9392-86fb764731c7.png"></p>
<p>另外，<code>react</code>中支持数组的返回，像这样：</p>
<pre><code class="hljs">    return [
        &lt;p key=&#39;1&#39;&gt;我是小杜杜&lt;/p&gt;,
       &lt;p key=&#39;2&#39;&gt;React&lt;/p&gt;,
       &lt;p key=&#39;3&#39;&gt;Vue&lt;/p&gt;
    ]
复制代码
</code></pre>
<p>还有我们在进行数组遍历的时候，<code>React</code>都会在底层处理，在外部嵌套一个<code>&lt;React.Fragment /&gt;</code></p>
<h3 id="1-5-1-Fragment-与-lt-gt-lt-gt-的不同"><a href="#1-5-1-Fragment-与-lt-gt-lt-gt-的不同" class="headerlink" title="1.5.1 Fragment 与 &lt;&gt;&lt;/&gt;的不同"></a>1.5.1 Fragment 与 <code>&lt;&gt;&lt;/&gt;</code>的不同</h3><p>我们都知道<code>&lt;&gt;&lt;/&gt;</code>是<code>&lt;Fragment&gt;&lt;/Fragment&gt;</code>的简写，从原则上来说是一致的，那么你知道他们又什么不同吗？</p>
<p>实际上，<code>Fragment</code> 这个组件可以赋值 <code>key</code>，也就是索引，<code>&lt;&gt;&lt;/&gt;</code>不能赋值，应用在遍历数组上，有感兴趣的同学可以试一试～</p>
<h2 id="1-6-lazy"><a href="#1-6-lazy" class="headerlink" title="1.6 lazy"></a>1.6 lazy</h2><p><strong>lazy</strong>：允许你定义一个动态加载组件，这样有助于缩减 bundle 的体积，并延迟加载在初次渲染时未用到的组件，也就是懒加载组件（高阶组件）</p>
<p><code>lazy</code>接收一个函数，这个函数需要动态调用<code>import()</code>,如：</p>
<pre><code class="hljs">const LazyChild = lazy(() =&gt; import(&#39;./child&#39;));
复制代码
</code></pre>
<p>那么<code>import(&#39;./child&#39;)</code>是一个怎样的类型呢？</p>
<p>实际上<code>lazy</code>必须接受一个函数，并且需要返回一个<code>Promise</code>, 并且需要<code>resolve</code>一个<code>default</code>一个<code>React</code>组件，除此之外，<code>lazy</code>必须要配合<code>Suspense</code>一起使用</p>
<p>举个例子🌰：我加入了<code>setTimeout</code>方便看到更好的效果：</p>
<pre><code class="hljs">import React, &#123; Component, Suspense, lazy &#125; from &#39;react&#39;;
import Child from &#39;./child&#39;
import &#123; Button, DotLoading &#125; from &#39;antd-mobile&#39;;

const LazyChild = lazy(() =&gt; new Promise((res) =&gt; &#123;
    setTimeout(() =&gt; &#123;
        res(&#123;
            default: () =&gt; &lt;Child /&gt;
        &#125;)
    &#125;, 1000)
&#125;))

class Index extends Component&#123;

  constructor(props)&#123;
    super(props)
    this.state=&#123;
        show: false
    &#125;
  &#125;

  render()&#123;
    const &#123; show &#125; = this.state
    return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
        &lt;Button color=&#39;primary&#39; onClick=&#123;() =&gt; this.setState(&#123; show: true &#125;)&#125; &gt;
            渲染
        &lt;/Button&gt;
        &#123;
            show &amp;&amp; &lt;Suspense fallback=&#123;&lt;div&gt;&lt;DotLoading color=&#39;primary&#39; /&gt;加载中&lt;/div&gt;&#125;&gt;
            &lt;LazyChild /&gt;
        &lt;/Suspense&gt;
        &#125;
    &lt;/div&gt;
  &#125;
&#125;

export default Index;
复制代码
</code></pre>
<p>Child 文件：</p>
<pre><code class="hljs">import React, &#123; useEffect &#125; from &#39;react&#39;;
import img from &#39;./img.jpeg&#39;

const Index = () =&gt; &#123;

  useEffect(() =&gt; &#123;
    console.log(&#39;照片渲染&#39;)
  &#125;, [])

  return &lt;div&gt;
  &lt;img src=&#123;img&#125; width=&#123;200&#125; height=&#123;160&#125; /&gt;
&lt;/div&gt;
&#125;

export default Index;
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4c0e47787a5e4e02ba5472625d20ce86~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img5.gif"></p>
<h2 id="1-7-Suspense"><a href="#1-7-Suspense" class="headerlink" title="1.7 Suspense"></a>1.7 Suspense</h2><p><strong>Suspense</strong>：让组件”等待”某个异步组件操作，直到该异步操作结束即可渲染。</p>
<p>与上面<code>lazy</code>中的案例一样，两者需要配合使用，其中<code>fallback</code>为等待时渲染的样式</p>
<p><code>Suspense</code>和<code>lazy</code>可以用于等待照片、脚本和一些异步的情况。</p>
<h2 id="1-8-Profiler"><a href="#1-8-Profiler" class="headerlink" title="1.8 Profiler"></a>1.8 Profiler</h2><p><strong>Profiler</strong>：这个组件用于性能检测，可以检测一次<code>react</code>组件渲染时的性能开销</p>
<p>此组件有两个参数：</p>
<ul>
<li>  <code>id</code>：标识<code>Profiler</code>的唯一性</li>
<li>  <code>onRender</code>：回调函数，用于渲染完成，参数在下面讲解</li>
</ul>
<p>举个栗子🌰：</p>
<pre><code class="hljs">import React, &#123; Component, Profiler &#125; from &#39;react&#39;;



export default Index;
复制代码
</code></pre>
<p>让我们来看看打印的是什么：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8bd6129306444e6281987f9ee05c7668~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>依此的含义：</p>
<ul>
<li>  <strong>id</strong>：<code>Profiler</code>树的<code>id</code></li>
<li>  <strong>phase</strong>：<code>mount</code>挂载，<code>update</code>渲染</li>
<li>  <strong>actualDuration</strong>：更新 <code>committed</code> 花费的渲染时间</li>
<li>  <strong>baseDuration</strong>：渲染整颗子树需要的时间</li>
<li>  <strong>startTime</strong>：更新开始渲染的时间</li>
<li>  <strong>commitTime</strong>：更新 <code>committed</code> 的时间</li>
<li>  <strong>interactions</strong>：本次更新的 <code>interactions</code> 的集合</li>
</ul>
<p>需要注意的是，这个组件应该在需要的时候去使用，虽然<code>Profiler</code>是一个轻量级的，但也会带来负担</p>
<h2 id="1-9-StrictMode"><a href="#1-9-StrictMode" class="headerlink" title="1.9 StrictMode"></a>1.9 StrictMode</h2><p><strong>StrictMode</strong>：严格模式，是一种用于突出显示应用程序中潜在问题的工具</p>
<p>与<code>Fragment</code>一样，<code>StrictMode</code>也不会出现在<code>UI</code>层面，只是会检查和警告</p>
<p>可以看一下官方的示例：</p>
<pre><code class="hljs">import React from &#39;react&#39;;

function ExampleApplication() &#123;
  return (
    &lt;div&gt;
      &lt;Header /&gt;
      &lt;React.StrictMode&gt;        &lt;div&gt;
          &lt;ComponentOne /&gt;
          &lt;ComponentTwo /&gt;
        &lt;/div&gt;
      &lt;/React.StrictMode&gt;      &lt;Footer /&gt;
    &lt;/div&gt;
  );
&#125;
复制代码
</code></pre>
<p>上述代码中只会对<code>ComponentOne</code>和<code>ComponentTwo</code>进行检查</p>
<p>主要有以下帮助：</p>
<ul>
<li>  <a href="https://link.juejin.cn/?target=https://reactjs.org/docs/strict-mode.html%23identifying-unsafe-lifecycles" title="https://reactjs.org/docs/strict-mode.html#identifying-unsafe-lifecycles">识别具有不安全生命周期的组件</a></li>
<li>  <a href="https://link.juejin.cn/?target=https://reactjs.org/docs/strict-mode.html%23warning-about-legacy-string-ref-api-usage" title="https://reactjs.org/docs/strict-mode.html#warning-about-legacy-string-ref-api-usage">关于旧版字符串引用 API 使用的警告</a></li>
<li>  <a href="https://link.juejin.cn/?target=https://reactjs.org/docs/strict-mode.html%23warning-about-deprecated-finddomnode-usage" title="https://reactjs.org/docs/strict-mode.html#warning-about-deprecated-finddomnode-usage">关于不推荐使用 findDOMNode 的警告</a></li>
<li>  <a href="https://link.juejin.cn/?target=https://reactjs.org/docs/strict-mode.html%23detecting-unexpected-side-effects" title="https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects">检测意外的副作用</a></li>
<li>  <a href="https://link.juejin.cn/?target=https://reactjs.org/docs/strict-mode.html%23detecting-legacy-context-api" title="https://reactjs.org/docs/strict-mode.html#detecting-legacy-context-api">检测遗留上下文 API</a></li>
<li>  <a href="https://link.juejin.cn/?target=https://reactjs.org/docs/strict-mode.html%23ensuring-reusable-state" title="https://reactjs.org/docs/strict-mode.html#ensuring-reusable-state">确保可重用状态</a></li>
</ul>
<h1 id="2-工具类"><a href="#2-工具类" class="headerlink" title="2 工具类"></a>2 工具类</h1><h2 id="2-1-crateElement"><a href="#2-1-crateElement" class="headerlink" title="2.1 crateElement"></a>2.1 crateElement</h2><p>在之前讲的<a target="_blank" rel="noopener" href="https://juejin.cn/post/7116326409961734152" title="https://juejin.cn/post/7116326409961734152">虚拟DOM</a>的时候已经详细的讲解过，我们在这里在在复习一遍</p>
<p><code>JSX</code>会被编译为<code>React.createElement</code>的形式，然后被<code>babel</code>编译</p>
<p>结构：</p>
<p><code>React.createElement(type, [props], [...children])</code>共有三个参数：</p>
<ul>
<li>  <code>type</code>：原生组件的话是标签的字符串，如<code>“div”</code>，如果是<code>React</code>自定义组件，则会传入组件</li>
<li>  <code>[props]</code>：对象，<code>dom</code>类中的<code>属性</code>，<code>组件</code>中的<code>props</code></li>
<li>  <code>[...children]</code>：其他的参数，会依此排序</li>
</ul>
<p>举个例子🌰： 举个栗子🌰：</p>
<pre><code class="hljs">    class Info extends React.Component &#123;
        render()&#123;
            return(
                &lt;div&gt;
                    Hi！我是小杜杜
                    &lt;p&gt;欢迎&lt;/p&gt;
                    &lt;Children&gt;我是子组件&lt;/Children&gt;
                &lt;/div&gt;
            )
        &#125;
    &#125;
复制代码
</code></pre>
<p>上述代码会被翻译为：</p>
<pre><code class="hljs">    class Info extends React.Component &#123;
        render()&#123;
            return React.createElement(
                &#39;div&#39;, 
                null, 
                &quot;Hi！我是小杜杜&quot;,
                React.createElement(&#39;p&#39;, null, &#39;欢迎&#39;)， // 原生标签
                React.createElement( 
                    Children, //自定义组件
                    null, // 属性
                    &#39;我是子组件&#39;  //child文本内容
                )
            )
        &#125;
    &#125;
复制代码
</code></pre>
<h3 id="2-1-1-注意点"><a href="#2-1-1-注意点" class="headerlink" title="2.1.1 注意点"></a>2.1.1 注意点</h3><ul>
<li>  <code>JSX</code>的结构实际上和<code>React.createElement</code>写法一致，只是用<code>JSX</code>更加简单、方便</li>
<li>  经过<code>React.createElement</code>的包裹，最终会形成 <code>$$typeof = Symbol(react.element)</code>对象，对象保存了<code>react.element</code>的信息。</li>
</ul>
<h2 id="2-2-cloneElement"><a href="#2-2-cloneElement" class="headerlink" title="2.2 cloneElement"></a>2.2 cloneElement</h2><p><code>cloneElement</code>：克隆并返回一个新的<code>React</code>元素,</p>
<p>结构： <code>React.createElement(type, [props], [...children])</code></p>
<p><code>React.cloneElement()</code>几乎等同于:</p>
<pre><code class="hljs">&lt;element.type &#123;...element.props&#125; &#123;...props&#125;&gt;
    &#123;children&#125;
&lt;/element.type&gt;
复制代码
</code></pre>
<p>举个例子🌰：</p>
<pre><code class="hljs">import React from &#39;react&#39;;

const Child = () =&gt; &#123;
  const children = React.cloneElement(&lt;div&gt;大家好，我是小杜杜&lt;/div&gt;, &#123;name: &#39;小杜杜&#39;&#125;)
  console.log(children)
  return &lt;div&gt;&#123;children&#125;&lt;/div&gt;
&#125;

const Index = () =&gt; &#123;

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;Child /&gt;
  &lt;/div&gt;
&#125;

export default Index;
复制代码
</code></pre>
<p>打印下<code>children</code>来看看：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1eac481267424eba9a0115b5b2d6522c~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>其实是可以看到传递的<code>name</code>的，也就是说可以通过<code>React.cloneElement</code>方法去对组件进行一些赋能</p>
<h2 id="2-3-createContext"><a href="#2-3-createContext" class="headerlink" title="2.3 createContext"></a>2.3 createContext</h2><p><strong>createContext</strong>：相信大家对这个Api很熟悉，用于传递上下文。<code>createContext</code>会创建一个<code>Context</code>对象，用<code>Provider</code>的<code>value</code>来传递值，用<code>Consumer</code>接受<code>value</code></p>
<p>我们实现一个父传孙的小栗子🌰：</p>
<pre><code class="hljs">import React, &#123; useState &#125; from &#39;react&#39;;

const Content = React.createContext()


const Child = () =&gt; &#123;
  return &lt;Content.Consumer&gt;
    &#123;(value) =&gt; &lt;Son &#123;...value&#125; /&gt;&#125;
  &lt;/Content.Consumer&gt;
&#125;

const Son = (props) =&gt; &#123;
  return &lt;&gt;
    &lt;div&gt;大家好，我是&#123;props.name&#125;&lt;/div&gt;
    &lt;div&gt;幸运数字是：&#123;props.number&#125;&lt;/div&gt;
  &lt;/&gt;
&#125;

const Index = () =&gt; &#123;

  const [data, _] = useState(&#123;
    name: &#39;小杜杜&#39;,
    number: 7
  &#125;)

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;Content.Provider value=&#123;data&#125;&gt;
      &lt;Child /&gt;
    &lt;/Content.Provider&gt;
  &lt;/div&gt;
&#125;

export default Index;
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1572ec2259fb4e49af43e9021ebf852d~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>注意：如果<code>Consumer</code>上一级一直没有<code>Provider</code>,则会应用<code>defaultValue</code>作为<code>value</code>。</p>
<p>只有当组件所处的树中没有匹配到 <code>Provider</code> 时，其 <code>defaultValue</code> 参数才会生效。</p>
<h2 id="2-4-Children"><a href="#2-4-Children" class="headerlink" title="2.4 Children"></a>2.4 Children</h2><p><strong>Children</strong>: 提供处理<code>this.props.children</code>不透明数据结构的实用程序.</p>
<p>那么什么是<code>不透明</code>的数据呢？</p>
<p>先来看看下面的栗子🌰：</p>
<pre><code class="hljs">import React, &#123; useEffect &#125; from &#39;react&#39;;

const Child = (&#123;children&#125;) =&gt; &#123;
  console.log(children)
  return children
&#125;

const Index = () =&gt; &#123;

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;Child&gt;
      &lt;p&gt;大家好，我是小杜杜&lt;/p&gt;
      &lt;p&gt;大家好，我是小杜杜&lt;/p&gt;
      &lt;p&gt;大家好，我是小杜杜&lt;/p&gt;
      &lt;p&gt;Hello～&lt;/p&gt;
    &lt;/Child&gt;
  &lt;/div&gt;
&#125;

export default Index;
复制代码
</code></pre>
<p>打印下<code>children</code>看到：</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ef75bab50b1a47e2b4cd5a1b75387eb3~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>我们可以看到每个节点都打印出来了，这种情况属于<code>透明的</code>，但我们要是便利看看：</p>
<pre><code class="hljs">&lt;Child&gt;
      &#123;
        [1,2,3].map((item) =&gt; &lt;p key=&#123;item&#125;&gt;大家好，我是小杜杜&lt;/p&gt;)
      &#125;
  &lt;p&gt;Hello～&lt;/p&gt;
&lt;/Child&gt;
复制代码
</code></pre>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dd56173a0c05431e8295c363b2e69d5a~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>却发现我们便利的三个元素被包了一层，像这种数据被称为<code>不透明</code>，我们想要处理这种数据，就要以来<code>React.Chilren</code> 来解决</p>
<h3 id="2-4-1-Children-map"><a href="#2-4-1-Children-map" class="headerlink" title="2.4.1 Children.map"></a>2.4.1 Children.map</h3><p><code>Children.map</code>：遍历，并返回一个数组，针对上面的情况，我们可以通过这个方法将数据便会原先的</p>
<pre><code class="hljs">const Child = (&#123;children&#125;) =&gt; &#123;
  const res = React.Children.map(children, (item) =&gt; item)
  console.log(res)
  return res
&#125;
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/887f747c02d94a2e939cda97bb54489e~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<h3 id="2-4-2-Children-forEach"><a href="#2-4-2-Children-forEach" class="headerlink" title="2.4.2 Children.forEach"></a>2.4.2 Children.forEach</h3><p><code>Children.forEach</code>：与<code>Children.map</code>类似，不同的是<code>Children.forEach</code>并不会返回值，而是停留在遍历阶段</p>
<pre><code class="hljs">const Child = (&#123;children&#125;) =&gt; &#123;
  React.Children.forEach(children, (item) =&gt; console.log(item))
  return children
&#125;
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dd7219448191481d8c9c22c0b0008248~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<h3 id="2-4-3-Children-count"><a href="#2-4-3-Children-count" class="headerlink" title="2.4.3 Children.count"></a>2.4.3 Children.count</h3><p><strong>Children.count</strong>：返回Child内的总个数，等于回调传递给<code>map</code>或<code>forEach</code>将被调用的次数。如：</p>
<pre><code class="hljs">const Child = (&#123;children&#125;) =&gt; &#123;
  const res =  React.Children.count(children)
  console.log(res) // 4
  return children
&#125;
复制代码
</code></pre>
<h3 id="2-4-4-Children-only"><a href="#2-4-4-Children-only" class="headerlink" title="2.4.4 Children.only"></a>2.4.4 Children.only</h3><p><strong>Children.only</strong>：验证<code>Child</code>是否只有一个元素，如果是，则正常返回，如果不是，则会报错。🤷‍♂不知道这个有啥用～</p>
<pre><code class="hljs">const Child = (&#123;children&#125;) =&gt; &#123;
  console.log(React.Children.only(children))
  return children
&#125;
复制代码
</code></pre>
<p>效果： 只有一个时：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f88f77f54f204b038d8ba7d57258d13f~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>多个时： <img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2af6da9b7dfc4dcc98d44f5b042feb5d~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<h3 id="2-4-5-Children-toArray"><a href="#2-4-5-Children-toArray" class="headerlink" title="2.4.5 Children.toArray"></a>2.4.5 Children.toArray</h3><p><strong>Children.toArray</strong>：以平面数组的形式返回<code>children</code>不透明数据结构，每个子元素都分配有键。</p>
<p>如果你想在你的渲染方法中操作子元素的集合，特别是如果你想<code>this.props.children</code>在传递它之前重新排序或切片，这很有用。</p>
<p>我们在原先的例子上在加一次来看看：</p>
<pre><code class="hljs">import React from &#39;react&#39;;

const Child = (&#123;children&#125;) =&gt; &#123;
  console.log(`原来数据:`, children)
  const res = React.Children.toArray(children)
  console.log(`扁平后的数据:`, res)
  return res
&#125;

const Index = () =&gt; &#123;

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;Child&gt;
      &#123;
        [1,2,3].map((item) =&gt; [5, 6].map((ele) =&gt; &lt;p key=&#123;`$&#123;item&#125;-$&#123;ele&#125;`&#125;&gt;大家好，我是小杜杜&lt;/p&gt;))
      &#125;
      &lt;p&gt;Hello～&lt;/p&gt;
    &lt;/Child&gt;
  &lt;/div&gt;
&#125;

export default Index;
复制代码
</code></pre>
<p>效果： <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/916ea01d44af45f0b1aee498918b040b~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>这里需要注意的是<code>key</code>,经过<code>Children.toArray</code>处理后，会给原本的<code>key</code>添加前缀，以使得每个元素 <code>key</code> 的范围都限定在此函数入参数组的对象内。</p>
<h2 id="2-5-createRef"><a href="#2-5-createRef" class="headerlink" title="2.5 createRef"></a>2.5 createRef</h2><p><strong>createRef</strong>：创建一个<code>ref</code>对象，获取节点信息，直接举个例子：</p>
<pre><code class="hljs">import React, &#123; Component &#125; from &#39;react&#39;;

class Index extends Component&#123;
  constructor(props)&#123;
      super(props)
  &#125;

  node = React.createRef()

  componentDidMount()&#123;
    console.log(this.node)
  &#125;
  render()&#123;
    return &lt;div ref=&#123;this.node&#125; &gt; 节点信息 &lt;/div&gt;
  &#125;
&#125;

export default Index;
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f3d2ea557dfc4f41ababbc2bf855e797~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>这个有点鸡肋，因为我们可以直接从<code>ref</code>上获取到值，没有必要通过<code>createRef</code>去获取，像这样</p>
<pre><code class="hljs">import React, &#123; Component &#125; from &#39;react&#39;;

class Index extends Component&#123;
  constructor(props)&#123;
      super(props)
  &#125;

  node = null

  componentDidMount()&#123;
    console.log(this.node)
  &#125;
  render()&#123;
    return &lt;div ref=&#123;(node) =&gt; this.node = node&#125; &gt; 节点信息 &lt;/div&gt;
  &#125;
&#125;

export default Index;
复制代码
</code></pre>
<h2 id="2-6-createFactory"><a href="#2-6-createFactory" class="headerlink" title="2.6 createFactory"></a>2.6 createFactory</h2><p><strong>createFactory</strong>：返回一个生成给定类型的 React 元素的函数。</p>
<p>接受一个参数<code>type</code>，这个<code>type</code>与<code>createElement</code>的<code>type</code>一样，原生组件的话是标签的字符串，如<code>“div”</code>，如果是<code>React</code>自定义组件，则会传入组件</p>
<p>效果与<code>createElement</code>一样，但这个说是遗留的，官方建议直接使用<code>createElement</code>，并且在使用上也会给出警告</p>
<p>栗子🌰：</p>
<pre><code class="hljs">import React, &#123; useEffect &#125; from &#39;react&#39;;

const Child = React.createFactory(()=&gt;&lt;div&gt;createFactory&lt;/div&gt;) 

const Index = () =&gt; &#123;
  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    大家好，我是小杜杜
    &lt;Child /&gt;
  &lt;/div&gt;
&#125;

export default Index;
复制代码
</code></pre>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/da152abddee94cfba0d3755e8ea4fa75~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<h2 id="2-7-isValidElement"><a href="#2-7-isValidElement" class="headerlink" title="2.7 isValidElement"></a>2.7 isValidElement</h2><p><strong>isValidElement</strong>：用于验证是否是React元素，是的话就返回<code>true</code>，否则返回<code>false</code>，感觉这个<code>Api</code>也不是特别有用，因为我们肯定知道是否是</p>
<p>栗子🌰：</p>
<pre><code class="hljs">    console.log(React.isValidElement(&lt;div&gt;大家好，我是小杜杜&lt;/div&gt;)) // true
    console.log(React.isValidElement(&#39;大家好，我是小杜杜&#39;)) //false
复制代码
</code></pre>
<h2 id="2-8version"><a href="#2-8version" class="headerlink" title="2.8version"></a>2.8version</h2><p>查看React的版本号：如：</p>
<pre><code class="hljs">console.log(React.version)
复制代码
</code></pre>
<p>版本：</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8f31bf4a9a624b31b247a96df1ca0193~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>我们可以看下在<code>React</code>中的文件位置，在<code>react</code>中有一个单独处理版本信息的位置：</p>
<p><code>packages/shared/ReactVersion.js</code></p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4ab9a692c09948e4a423310164a91932~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<h1 id="3-生命周期"><a href="#3-生命周期" class="headerlink" title="3 生命周期"></a>3 生命周期</h1><p><code>React</code> 的 生命周期主要有两个比较大的版本，分别是<code>v16.0前</code>和<code>v16.4</code>两个版本的生命周期，我们分别说下旧的和新的生命周期，做下对比～</p>
<h2 id="3-1-v16-0前"><a href="#3-1-v16-0前" class="headerlink" title="3.1 v16.0前"></a>3.1 v16.0前</h2><p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/99f1eddd84e94273a098640d0d148a21~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>从图中，总共分为四大阶段：<code>Intialization(初始化)</code>、<code>Mounting(挂载)</code>、<code>Update(更新)</code>、<code>Unmounting(卸载)</code></p>
<h3 id="3-1-1-Intialization-初始化）"><a href="#3-1-1-Intialization-初始化）" class="headerlink" title="3.1.1 Intialization(初始化）"></a>3.1.1 Intialization(初始化）</h3><p>在初始化阶段,我们会用到 <code>constructor()</code> 这个构造函数，如：</p>
<pre><code class="hljs">constructor(props) &#123;
  super(props);
&#125;
复制代码
</code></pre>
<ul>
<li>  <code>super的作用</code>“ 用来调用基类的构造方法( constructor() ), 也将父组件的props注入给子组件，供子组件读取 (组件中<code>props只读不可变``，state可变</code>)</li>
<li>  初始化操作，定义this.state的初始内容</li>
<li>  只会执行一次</li>
</ul>
<h3 id="3-1-2-Mounting-挂载）"><a href="#3-1-2-Mounting-挂载）" class="headerlink" title="3.1.2 Mounting(挂载）"></a>3.1.2 Mounting(挂载）</h3><p><strong>componentWillMount</strong>：在组件挂载到DOM前调用</p>
<ul>
<li>  这里面的调用的<code>this.setState</code>不会引起组件的重新渲染，也可以把写在这边的内容提到<code>constructor()</code>，所以在项目中很少。</li>
<li>  只会调用一次</li>
</ul>
<p><strong>render</strong>: 渲染</p>
<ul>
<li>  只要<code>props</code>和<code>state</code>发生<code>改变</code>（无两者的重传递和重赋值，论值是否有变化，都可以引起组件重新render），都会重新渲染render。</li>
<li>  <code>return</code>：是必须的，是一个React元素（UI，描述组件），不负责组件实际渲染工作，由React自身根据此元素去渲染出DOM。</li>
<li>  <code>render</code>是<strong>纯函数</strong>（Pure function：返回的结果只依赖与参数，执行过程中没有副作用），不能执行this.setState。</li>
</ul>
<p><strong>componentDidMount</strong>：组件挂载到DOM后调用</p>
<ul>
<li>  调用一次</li>
</ul>
<h3 id="3-1-3-Update-更新"><a href="#3-1-3-Update-更新" class="headerlink" title="3.1.3 Update(更新)"></a>3.1.3 Update(更新)</h3><p><strong>componentWillReceiveProps(nextProps)</strong>:调用于props引起的组件更新过程中</p>
<ul>
<li>  <code>nextProps</code>：父组件传给当前组件新的props</li>
<li>  可以用<code>nextProps</code>和<code>this.props</code>来查明重传props是否发生改变（原因：不能保证父组件重传的props有变化）</li>
<li>  只要props发生变化就会，引起调用</li>
</ul>
<p>**shouldComponentUpdate(nextProps, nextState)**：性能优化组件</p>
<ul>
<li><strong>nextProps</strong>：当前组件的this.props</li>
<li><strong>nextState</strong>：当前组件的this.state</li>
<li>通过比较<code>nextProps</code>和<code>nextState</code>,来判断当前组件是否有必要继续执行更新过程。  返回false：表示停止更新，用于减少组件的不必要渲染，优化性能  返回true：继续执行更新</li>
<li>像<code>componentWillReceiveProps（）</code>中执行了this.setState，更新了state，但在<code>render</code>前(如shouldComponentUpdate，componentWillUpdate)，this.state依然指向更新前的state，不然nextState及当前组件的this.state的对比就一直是true了</li>
</ul>
<p>**componentWillUpdate(nextProps, nextState)**：组件更新前调用</p>
<ul>
<li>  在render方法前执行</li>
<li>  由于组件更新就会调用，所以一般很少使用</li>
<li>  render：重新渲染</li>
</ul>
<p>**componentDidUpdate(prevProps, prevState)**：组件更新后被调用</p>
<ul>
<li>  prevProps：组件更新前的props</li>
<li>  prevState：组件更新前的state</li>
<li>  可以操作组件更新的DOM</li>
</ul>
<h3 id="3-1-4-Unmounting-卸载"><a href="#3-1-4-Unmounting-卸载" class="headerlink" title="3.1.4 Unmounting(卸载)"></a>3.1.4 Unmounting(卸载)</h3><p><strong>componentWillUnmount</strong>：组件被卸载前调用</p>
<ul>
<li>  可以在这里执行一些清理工作，比如清楚组件中使用的定时器，清楚componentDidMount中手动创建的DOM元素等，以避免引起内存泄漏</li>
</ul>
<h2 id="3-2-React-v16-4"><a href="#3-2-React-v16-4" class="headerlink" title="3.2 React v16.4"></a>3.2 React v16.4</h2><p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/93f9eb88f8824ba2ab44897471225596~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="12b.png"></p>
<p>与 v16.0的生命周期相比</p>
<p>新增了 <strong>getDerivedStateFromProps</strong> 和<strong>getSnapshotBeforeUpdate</strong></p>
<p>取消了 <strong>componentWillMount</strong>、<strong>componentWillReceiveProps</strong>、<strong>componentWillUpdate</strong></p>
<h3 id="3-2-1-getDerivedStateFromProps"><a href="#3-2-1-getDerivedStateFromProps" class="headerlink" title="3.2.1 getDerivedStateFromProps"></a>3.2.1 getDerivedStateFromProps</h3><p>**getDerivedStateFromProps(prevProps, prevState)**：组件创建和更新时调用的方法</p>
<ul>
<li>  <code>prevProps</code>：组件更新前的props</li>
<li>  <code>prevState</code>：组件更新前的state</li>
</ul>
<p>注意：在React v16.3中，在创建和更新时，只能是由父组件引发才会调用这个函数，在React v16.4改为无论是Mounting还是Updating，也无论是什么引起的Updating，全部都会调用。</p>
<p>有点类似于<code>componentWillReceiveProps</code>，不同的是<code>getDerivedStateFromProps</code>是一个静态函数，也就是这个函数不能通过this访问到class的属性，当然也不推荐使用</p>
<p>如果props传入的内容不需要影响到你的state，那么就需要返回一个null，这个返回值是必须的，所以尽量将其写到函数的末尾。</p>
<p>在组件创建时和更新时的render方法之前调用，它应该返回一个对象来更新状态，或者返回null来不更新任何内容。</p>
<h3 id="3-2-2-getSnapshotBeforeUpdate"><a href="#3-2-2-getSnapshotBeforeUpdate" class="headerlink" title="3.2.2 getSnapshotBeforeUpdate"></a>3.2.2 getSnapshotBeforeUpdate</h3><p><code>getSnapshotBeforeUpdate(prevProps,prevState)</code>:Updating时的函数，在render之后调用</p>
<ul>
<li>  <code>prevProps</code>：组件更新前的props</li>
<li>  <code>prevState</code>：组件更新前的state</li>
</ul>
<p>可以读取，但无法使用DOM的时候，在组件可以在可能更改之前从DOM捕获一些信息（例如滚动位置）</p>
<p>返回的任何指都将作为参数传递给<code>componentDidUpdate（)</code></p>
<h2 id="3-3-注意"><a href="#3-3-注意" class="headerlink" title="3.3 注意"></a>3.3 注意</h2><p>在17.0的版本，官方彻底废除 <code>componentWillMount</code>、<code>componentWillReceiveProps</code>、<code>componentWillUpdate</code></p>
<p>如果还想使用的话可以使用：<code>UNSAFE_componentWillMount()</code>、<code>UNSAFE_componentWillReceiveProps()</code>、<code>UNSAFE_componentWillUpdate()</code></p>
<p>对了，如果在面试的时候可能会问道有关生命周期的问题，建议各位小伙伴，将以上的生命周期都可说一说，然后做个对比，这样的话，效果肯定不错～</p>
<h1 id="4-react-hooks"><a href="#4-react-hooks" class="headerlink" title="4 react-hooks"></a>4 react-hooks</h1><p><code>react-hooks</code>是<code>React 16.8</code>的产物，给函数式组件赋上了<code>生命周期</code>，再经过三年多的时间，<code>函数式组件</code>已经逐渐取代了<code>类组件</code>，可以说是<code>React</code>开发者必备的技术</p>
<p>同时在<code>React v18</code>中又出现了一些<code>hooks</code>，今天我们将一起详细的看看，确保你能迅速掌握～</p>
<h2 id="4-1-React-v16-8中的hooks"><a href="#4-1-React-v16-8中的hooks" class="headerlink" title="4.1 React v16.8中的hooks"></a>4.1 React v16.8中的hooks</h2><h3 id="4-1-1-useState"><a href="#4-1-1-useState" class="headerlink" title="4.1.1 useState"></a>4.1.1 useState</h3><p><strong>useState</strong>：定义变量，可以理解为他是类组件中的<code>this.state</code></p>
<p>使用：</p>
<pre><code class="hljs">const [state, setState] = useState(initialState);
复制代码
</code></pre>
<ul>
<li>  <code>state</code>：目的是提供给 <code>UI</code>，作为渲染视图的数据源</li>
<li>  <code>setState</code>：改变 state 的函数，可以理解为<code>this.setState</code></li>
<li>  <code>initialState</code>：初始默认值</li>
</ul>
<p>在这里我介绍两种写法，直接看栗子🌰：</p>
<pre><code class="hljs">import React, &#123; useState &#125; from &#39;react&#39;;
import &#123; Button &#125; from &#39;antd-mobile&#39;;
 
const Index = () =&gt; &#123;

  const [ number, setNumber ] = useState(0)

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;div&gt;数字：&#123;number&#125;&lt;/div&gt;
    &lt;Button
      color=&#39;primary&#39;
      onClick=&#123;() =&gt; &#123;
        setNumber(number + 1) //第一种
      &#125;&#125;
    &gt;
      点击加1
    &lt;/Button&gt;

    &lt;Button
      color=&#39;primary&#39;
      style=&#123;&#123;marginLeft: 8&#125;&#125;
      onClick=&#123;() =&gt; &#123;
        setNumber((value) =&gt; value + 2) //第二种
      &#125;&#125;
    &gt;
      点击加2
    &lt;/Button&gt;
  &lt;/div&gt;

&#125;
 
export default Index
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2fa71030e4a5453390a4931db7e15f87~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img5.gif"></p>
<h4 id="注意点"><a href="#注意点" class="headerlink" title="注意点"></a>注意点</h4><p><code>useState</code>有点类似于<code>PureComponent</code>,会进行一个比较浅的比较，如果是对象的时候直接传入并不会更新，这点一定要切记，如：</p>
<pre><code class="hljs">import React, &#123; useState &#125; from &#39;react&#39;;
import &#123; Button &#125; from &#39;antd-mobile&#39;;
 
const Index = () =&gt; &#123;

  const [ state, setState ] = useState(&#123;number: 0&#125;)

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;div&gt;数字：&#123;state.number&#125;&lt;/div&gt;
    &lt;Button
      color=&#39;primary&#39;
      onClick=&#123;() =&gt; &#123;
        state.number++
        setState(state)
      &#125;&#125;
    &gt;
      点击
    &lt;/Button&gt;
  &lt;/div&gt;
&#125;
 
export default Index
复制代码
</code></pre>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6c84752c460f4104a2d571b73ef9c7ba~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img1.gif"></p>
<h3 id="4-1-2-useEffect"><a href="#4-1-2-useEffect" class="headerlink" title="4.1.2 useEffect"></a>4.1.2 useEffect</h3><p><code>useEffect</code>：副作用，你可以理解为是类组件的生命周期，也是我们最常用的钩子</p>
<p>那么什么是副作用呢？ <strong>副作用（Side Effect</strong>：是指 function 做了和本身运算返回值无关的事，如请求数据、修改全局变量，打印、数据获取、设置订阅以及手动更改 <code>React</code> 组件中的 <code>DOM</code> 都属于副作用操作都算是副作用</p>
<p>我们直接演示下它的用法栗子🌰：</p>
<h4 id="不断执行"><a href="#不断执行" class="headerlink" title="不断执行"></a>不断执行</h4><p>当<code>useEffect</code>不设立第二个参数时，无论什么情况，都会执行</p>
<h4 id="模拟初始化和卸载"><a href="#模拟初始化和卸载" class="headerlink" title="模拟初始化和卸载"></a>模拟初始化和卸载</h4><p>我们可以利用<code>useEffect</code>弄<code>挂载</code>和<code>卸载</code>阶段，通常我们用于监听<code>addEventListener</code>和<code>removeEventListener</code>的使用</p>
<pre><code class="hljs">import React, &#123; useState, useEffect &#125; from &#39;react&#39;;
import &#123; Button &#125; from &#39;antd-mobile&#39;;


const Child = () =&gt; &#123;

  useEffect(() =&gt; &#123;
    console.log(&#39;挂载&#39;)
    
    return () =&gt; &#123;
      console.log(&#39;卸载&#39;)
    &#125;
  &#125;, [])

  return &lt;div&gt;大家好，我是小杜杜&lt;/div&gt;
&#125;
 
const Index = () =&gt; &#123;

  const [ flag, setFlag ] = useState(false)

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;Button
      color=&#39;primary&#39;
      onClick=&#123;() =&gt; &#123;
        setFlag(v =&gt; !v)
      &#125;&#125;
    &gt;
     &#123;flag ? &#39;卸载&#39; : &#39;挂载&#39;&#125;
    &lt;/Button&gt;
    &#123;flag &amp;&amp; &lt;Child /&gt;&#125;
  &lt;/div&gt;
&#125;
 
export default Index
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/12a7e4b40008436daf0d7ebe45aa2c78~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img1.gif"></p>
<h4 id="根据依赖值改变"><a href="#根据依赖值改变" class="headerlink" title="根据依赖值改变"></a>根据依赖值改变</h4><p>我们可以设置<code>useEffect</code>的第二个值来改变</p>
<pre><code class="hljs">import React, &#123; useState, useEffect &#125; from &#39;react&#39;;
import &#123; Button &#125; from &#39;antd-mobile&#39;;

const Index = () =&gt; &#123;

  const [ number, setNumber ] = useState(0)
  const [ count, setCount ] = useState(0)

  useEffect(() =&gt; &#123;
    console.log(&#39;count改变才会执行&#39;)
  &#125;, [count])

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;div&gt;number: &#123;number&#125;   count: &#123;count&#125;&lt;/div&gt;
    &lt;Button
      color=&#39;primary&#39;
      onClick=&#123;() =&gt; setNumber(v =&gt; v + 1)&#125;
    &gt;
      number点击加1
    &lt;/Button&gt;
    &lt;Button
      color=&#39;primary&#39;
      style=&#123;&#123;marginLeft: 8&#125;&#125;
      onClick=&#123;() =&gt; setCount(v =&gt; v + 1)&#125;
    &gt;
      count点击加1
    &lt;/Button&gt;
  &lt;/div&gt;
&#125;
 
export default Index
复制代码
</code></pre>
<p>效果： <img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3ca2332ae29e4419978b2a778182f216~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img2.gif"></p>
<h3 id="4-1-3-useContent"><a href="#4-1-3-useContent" class="headerlink" title="4.1.3 useContent"></a>4.1.3 useContent</h3><p><strong>useContent</strong>：上下文，类似于<code>Context</code>：其本意就是设置全局共享数据，使所有组件可跨层级实现共享</p>
<p><code>useContent</code>的参数一般是由<code>createContext</code>的创建，通过 <code>CountContext.Provider</code> 包裹的组件，才能通过 <code>useContext</code> 获取对应的值</p>
<p>举个例子🌰：</p>
<pre><code class="hljs">import React, &#123; useState, createContext, useContext &#125; from &#39;react&#39;;
import &#123; Button &#125; from &#39;antd-mobile&#39;;

const CountContext = createContext(-1)

const Child = () =&gt; &#123;
  const count = useContext(CountContext)

  return &lt;div style=&#123;&#123;marginTop: 20&#125;&#125;&gt;
    子组件获取到的count: &#123;count&#125;
    &lt;Son /&gt;
  &lt;/div&gt;
&#125;

const Son = () =&gt; &#123;
  const count = useContext(CountContext)

  return &lt;div style=&#123;&#123;marginTop: 20&#125;&#125;&gt;
    孙组件获取到的count: &#123;count&#125;
  &lt;/div&gt;
&#125;

const Index = () =&gt; &#123;

  const [ count, setCount ] = useState(0)

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;div&gt;父组件：&#123;count&#125;&lt;/div&gt;
    &lt;Button
      color=&#39;primary&#39;
      onClick=&#123;() =&gt; setCount(v =&gt; v + 1)&#125;
    &gt;
      点击加1
    &lt;/Button&gt;
    &lt;CountContext.Provider value=&#123;count&#125;&gt;
      &lt;Child /&gt;
    &lt;/CountContext.Provider&gt;
  &lt;/div&gt;
&#125;

export default Index
复制代码
</code></pre>
<p>效果： <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c40ffff952f743e1b391af4da39e376e~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img3.gif"></p>
<h3 id="4-1-4-useReducer"><a href="#4-1-4-useReducer" class="headerlink" title="4.1.4 useReducer"></a>4.1.4 useReducer</h3><p><strong>useReducer</strong>：它类似于<code>redux</code>功能的api</p>
<p>结构：</p>
<pre><code class="hljs">const [state, dispatch] = useReducer(reducer, initialArg, init);
复制代码
</code></pre>
<ul>
<li>  <code>state</code>：更新后的<code>state</code>值</li>
<li>  <code>dispatch</code>：可以理解为和<code>useState</code>的<code>setState</code>一样的效果</li>
<li>  <code>reducer</code>：可以理解为<code>redux</code>的<code>reducer</code></li>
<li>  <code>initialArg</code>：初始值</li>
<li>  <code>init</code>：惰性初始化</li>
</ul>
<p>直接来看看栗子🌰：</p>
<pre><code class="hljs">import React, &#123; useReducer &#125; from &#39;react&#39;;
import &#123; Button &#125; from &#39;antd-mobile&#39;;

const Index = () =&gt; &#123;

  const [count, dispatch] = useReducer((state, action)=&gt; &#123;
    switch(action?.type)&#123;
      case &#39;add&#39;:
        return state + action?.payload;
      case &#39;sub&#39;:
        return state - action?.payload;
      default:
        return state;
    &#125;
  &#125;, 0);

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;div&gt;count：&#123;count&#125;&lt;/div&gt;
    &lt;Button
      color=&#39;primary&#39;
      onClick=&#123;() =&gt; dispatch(&#123;type: &#39;add&#39;, payload: 1&#125;)&#125;
    &gt;
      加1
    &lt;/Button&gt;
    &lt;Button
      color=&#39;primary&#39;
      style=&#123;&#123;marginLeft: 8&#125;&#125;
      onClick=&#123;() =&gt; dispatch(&#123;type: &#39;sub&#39;, payload: 1&#125;)&#125;
    &gt;
      减1
    &lt;/Button&gt;
  &lt;/div&gt;
&#125;
 
export default Index
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/26fc08702a3446b28ae23143fd2696fb~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img4.gif"></p>
<h3 id="4-1-5-useMemo"><a href="#4-1-5-useMemo" class="headerlink" title="4.1.5 useMemo"></a>4.1.5 useMemo</h3><p><strong>useMemo</strong>:与<code>memo</code>的理念上差不多，都是判断是否满足当前的限定条件来决定是否执行<code>callback</code>函数，而<code>useMemo</code>的第二个参数是一个数组，通过这个数组来判定是否更新回掉函数</p>
<p>当一个父组件中调用了一个子组件的时候，父组件的 state 发生变化，会导致父组件更新，而子组件虽然没有发生改变，但也会进行更新。</p>
<p>简单的理解下，当一个页面内容非常复杂，模块非常多的时候，函数式组件会<strong>从头更新到尾</strong>，只要一处改变，所有的模块都会进行刷新，这种情况显然是没有必要的。</p>
<p>我们理想的状态是各个模块只进行自己的更新，不要相互去影响，那么此时用<code>useMemo</code>是最佳的解决方案。</p>
<p>这里要尤其注意一点，<strong>只要父组件的状态更新，无论有没有对自组件进行操作，子组件都会进行更新</strong>，<code>useMemo</code>就是为了防止这点而出现的</p>
<p>为了更好的理解<code>useMemo</code>，我们来看下面一个小栗子🌰：</p>
<pre><code class="hljs">// usePow.ts
const Index = (list: number[]) =&gt; &#123;

  return list.map((item:number) =&gt; &#123;
    console.log(1)
    return Math.pow(item, 2)
  &#125;)
&#125;

export default Index;

// index.tsx
import &#123; Button &#125; from &#39;antd-mobile&#39;;
import React,&#123; useState &#125; from &#39;react&#39;;
import &#123; usePow &#125; from &#39;@/components&#39;;

const Index:React.FC&lt;any&gt; = (props)=&gt; &#123;
  const [flag, setFlag] = useState&lt;boolean&gt;(true)
  const data = usePow([1, 2, 3])
  
  return (
    &lt;div&gt;
      &lt;div&gt;数字：&#123;JSON.stringify(data)&#125;&lt;/div&gt;
      &lt;Button color=&#39;primary&#39; onClick=&#123;() =&gt; &#123;setFlag(v =&gt; !v)&#125;&#125;&gt;切换&lt;/Button&gt;
       &lt;div&gt;切换状态：&#123;JSON.stringify(flag)&#125;&lt;/div&gt;
    &lt;/div&gt;
  );
&#125;

export default Index;
复制代码
</code></pre>
<p>我们简单的写了个 <code>usePow</code>，我们通过 <code>usePow</code> 给所传入的数字平方, 用切换状态的按钮表示函数内部的状态，我们来看看此时的效果：</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2d21cc3b15e24a8fba7a7cf3f2a89d14~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img2.gif"></p>
<p>我们发现了一个问题，为什么点击切换按钮也会触发<code>console.log(1)</code>呢？</p>
<p>这样明显增加了性能开销，我们的理想状态肯定不希望做无关的渲染，所以我们做自定义 <code>hooks</code>的时候一定要注意，需要<strong>减少性能开销</strong>,我们为组件加入 <code>useMemo</code>试试：</p>
<pre><code class="hljs">    import &#123; useMemo &#125; from &#39;react&#39;;

    const Index = (list: number[]) =&gt; &#123;
      return useMemo(() =&gt; list.map((item:number) =&gt; &#123;
        console.log(1)
        return Math.pow(item, 2)
      &#125;), []) 
    &#125;
    export default Index;
复制代码
</code></pre>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d3e583fe589a4dacbb0a5a72b2e99cef~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img3.gif"></p>
<p>发现此时就已经解决了这个问题，不会在做相关的渲染了</p>
<h3 id="4-1-6-useCallback"><a href="#4-1-6-useCallback" class="headerlink" title="4.1.6 useCallback"></a>4.1.6 useCallback</h3><p><code>useCallback</code>与<code>useMemo</code>极其类似,可以说是一模一样，唯一不同的是<code>useMemo</code>返回的是函数运行的<strong>结果</strong>，而<code>useCallback</code>返回的是<strong>函数</strong></p>
<p>注意：这个函数是父组件传递子组件的一个函数，防止做无关的刷新，其次，这个组件必须配合<code>memo</code>,否则<strong>不但不会提升性能，还有可能降低性能</strong></p>
<pre><code class="hljs">      import React, &#123; useState, useCallback &#125; from &#39;react&#39;;
      import &#123; Button &#125; from &#39;antd-mobile&#39;;

      const MockMemo: React.FC&lt;any&gt; = () =&gt; &#123;
        const [count,setCount] = useState(0)
        const [show,setShow] = useState(true)

        const  add = useCallback(()=&gt;&#123;
          setCount(count + 1)
        &#125;,[count])

        return (
          &lt;div&gt;
            &lt;div style=&#123;&#123;display: 'flex', justifyContent: 'flex-start'&#125;&#125;&gt;
              &lt;TestButton title=&quot;普通点击&quot; onClick=&#123;() =&gt; setCount(count + 1) &#125;/&gt;
              &lt;TestButton title=&quot;useCallback点击&quot; onClick=&#123;add&#125;/&gt;
            &lt;/div&gt;
            &lt;div style=&#123;&#123;marginTop: 20&#125;&#125;&gt;count: &#123;count&#125;&lt;/div&gt;
            &lt;Button onClick=&#123;() =&gt; &#123;setShow(!show)&#125;&#125;&gt; 切换&lt;/Button&gt;
          &lt;/div&gt;
        )
      &#125;

      const TestButton = React.memo((props:any)=&gt;&#123;
        console.log(props.title)
        return &lt;Button color=&#39;primary&#39; onClick=&#123;props.onClick&#125; style=&#123;props.title === &#39;useCallback点击&#39; ? &#123;
        marginLeft: 20
        &#125; : undefined&#125;&gt;&#123;props.title&#125;&lt;/Button&gt;
      &#125;)

      export default MockMemo;
复制代码
</code></pre>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5c00efe7071b40c583a3d167d073a979~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img2.gif"></p>
<p>我们可以看到，当点击切换按钮的时候，没有经过 <code>useCallback</code>封装的函数会再次刷新，而经过 <code>useCallback</code>包裹的函数不会被再次刷新</p>
<p>有很多小伙伴有个误区，就是<code>useCallback</code>不能单独使用，必须要配合<code>memo</code>吗？</p>
<p>其实是这样的，你可以单独使用<code>useCallback</code>，但只用<code>useCallback</code>起不到优化的作用，反而会增加性能消耗</p>
<p>想之前讲的，<code>React.memo</code>会通过浅比较里面的<code>props</code>，如果没有<code>memo</code>，那么使用的<code>useCallback</code>也就毫无意义</p>
<p>因为<code>useCallback</code>本身是需要开销的，所以反而会增加性能的消耗</p>
<h3 id="4-1-7-useRef"><a href="#4-1-7-useRef" class="headerlink" title="4.1.7 useRef"></a>4.1.7 useRef</h3><p><strong>useRef</strong>： 可以获取当前元素的所有属性，并且返回一个可变的ref对象，并且这个对象<strong>只有current属性</strong>，可设置<code>initialValue</code></p>
<p>结构：</p>
<pre><code class="hljs">const refContainer = useRef(initialValue);
复制代码
</code></pre>
<p>有许多小伙伴只知道<code>useRef</code>可以获取对应元素的属性，但<code>useRef</code>还具备一个功能，就是<code>缓存数据</code>，接下来一起看看：</p>
<h4 id="通过useRef获取对应的属性值"><a href="#通过useRef获取对应的属性值" class="headerlink" title="通过useRef获取对应的属性值"></a>通过useRef获取对应的属性值</h4><p>栗子🌰：</p>
<pre><code class="hljs">import React, &#123; useState, useRef &#125; from &#39;react&#39;;

const Index:React.FC&lt;any&gt; = () =&gt; &#123;
  const scrollRef = useRef&lt;any&gt;(null);
  const [clientHeight, setClientHeight ] = useState&lt;number&gt;(0)
  const [scrollTop, setScrollTop ] = useState&lt;number&gt;(0)
  const [scrollHeight, setScrollHeight ] = useState&lt;number&gt;(0)

  const onScroll = () =&gt; &#123;
    if(scrollRef?.current)&#123;
      let clientHeight = scrollRef?.current.clientHeight; //可视区域高度
      let scrollTop  = scrollRef?.current.scrollTop;  //滚动条滚动高度
      let scrollHeight = scrollRef?.current.scrollHeight; //滚动内容高度
      setClientHeight(clientHeight)
      setScrollTop(scrollTop)
      setScrollHeight(scrollHeight)
    &#125;
  &#125;

  return (
    &lt;div &gt;
      &lt;div &gt;
        &lt;p&gt;可视区域高度：&#123;clientHeight&#125;&lt;/p&gt;
        &lt;p&gt;滚动条滚动高度：&#123;scrollTop&#125;&lt;/p&gt;
        &lt;p&gt;滚动内容高度：&#123;scrollHeight&#125;&lt;/p&gt;
      &lt;/div&gt;
      &lt;div style=&#123;&#123;height: 200, overflowY: 'auto'&#125;&#125; ref=&#123;scrollRef&#125; onScroll=&#123;onScroll&#125; &gt;
        &lt;div style=&#123;&#123;height: 2000&#125;&#125;&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
&#125;;

export default Index;
复制代码
</code></pre>
<p>效果： <img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ce0221becc3940ec8610783afa66f5dc~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img1.gif"></p>
<p>从上述可知，我们可以通过<code>useRef</code>来获取对应元素的相关属性，以此来做一些操作</p>
<h4 id="缓存数据"><a href="#缓存数据" class="headerlink" title="缓存数据"></a>缓存数据</h4><p><code>react-redux</code>的源码中，在hooks推出后，<code>react-redux</code>用大量的<strong>useMemo</strong>重做了<strong>Provide</strong>等核心模块，其中就是运用<strong>useRef</strong>来缓存数据，并且所运用的 <strong>useRef()</strong> 没有一个是绑定在dom元素上的，都是做数据缓存用的</p>
<p>可以简单的来看一下：</p>
<pre><code class="hljs">    // 缓存数据
    /* react-redux 用userRef 来缓存 merge之后的 props */ 
    const lastChildProps = useRef() 
    
    // lastWrapperProps 用 useRef 来存放组件真正的 props信息 
    const lastWrapperProps = useRef(wrapperProps) 
    
    //是否储存props是否处于正在更新状态 
    const renderIsScheduled = useRef(false)

    //更新数据
    function captureWrapperProps( 
        lastWrapperProps, 
        lastChildProps, 
        renderIsScheduled, 
        wrapperProps, 
        actualChildProps, 
        childPropsFromStoreUpdate, 
        notifyNestedSubs 
    ) &#123; 
        lastWrapperProps.current = wrapperProps 
        lastChildProps.current = actualChildProps 
        renderIsScheduled.current = false 
   &#125;
复制代码
</code></pre>
<p>我们看到 <code>react-redux</code> 用重新赋值的方法，改变了缓存的数据源，减少了不必要的更新，如过采取<code>useState</code>势必会重新渲染。</p>
<p>有的时候我们需要使用<strong>useMemo</strong>、<strong>useCallback</strong>Api，我们控制变量的值用<strong>useState</strong> 有可能会导致拿到的是旧值，并且如果他们更新会带来整个组件重新执行，这种情况下，我们使用<strong>useRef</strong>将会是一个非常不错的选择</p>
<h3 id="4-1-8-useImperativeHandle"><a href="#4-1-8-useImperativeHandle" class="headerlink" title="4.1.8 useImperativeHandle"></a>4.1.8 useImperativeHandle</h3><p><strong>useImperativeHandle</strong>：可以让你在使用 <code>ref</code> 时自定义暴露给父组件的实例值</p>
<p>这个Api我觉得是十分有用的，建议掌握哦，来看看使用的场景：</p>
<p>在一个页面很复杂的时候，我们会将这个页面进行模块化，这样会分成很多个模块，有的时候我们需要在<code>最外层的组件上</code>控制其他组件的方法，希望最外层的点击事件，同时执行<code>子组件的事件</code>，这时就需要 useImperativeHandle 的帮助</p>
<p>结构：</p>
<pre><code class="hljs">useImperativeHandle(ref, createHandle, [deps])
复制代码
</code></pre>
<ul>
<li>  <code>ref</code>：<code>useRef</code>所创建的ref</li>
<li>  <code>createHandle</code>：处理的函数，返回值作为暴露给父组件的 ref 对象。</li>
<li>  <code>deps</code>：依赖项，依赖项更改形成新的 ref 对象。</li>
</ul>
<p>举个栗子🌰：</p>
<pre><code class="hljs">import React, &#123; useState, useImperativeHandle, useRef &#125; from &#39;react&#39;;
import &#123; Button &#125; from &#39;antd-mobile&#39;;

const Child = (&#123;cRef&#125;) =&gt; &#123;

  const [count, setCount] = useState(0)

  useImperativeHandle(cRef, () =&gt; (&#123;
    add
  &#125;))

  const add = () =&gt; &#123;
    setCount((v) =&gt; v + 1)
  &#125;

  return &lt;div style=&#123;&#123;marginBottom: 20&#125;&#125;&gt;
    &lt;p&gt;点击次数：&#123;count&#125;&lt;/p&gt;
    &lt;Button color=&#39;primary&#39; onClick=&#123;() =&gt; add()&#125;&gt;加1&lt;/Button&gt;
  &lt;/div&gt;
&#125;

const Index = () =&gt; &#123;
  const ref = useRef(null)

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;div&gt;大家好，我是小杜杜&lt;/div&gt;
    &lt;div&gt;注意:是在父组件上的按钮，控制子组件的加1哦～&lt;/div&gt;
    &lt;Button
      color=&#39;primary&#39;
      onClick=&#123;() =&gt;  ref.current.add()&#125;
    &gt;
      父节点上的加1
    &lt;/Button&gt;
    &lt;Child cRef=&#123;ref&#125; /&gt;
  &lt;/div&gt;
&#125;
 
export default Index
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/76e810c5966a49c2bd9ce8f505f5b18c~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img5.gif"></p>
<h3 id="4-1-9-useLayoutEffect"><a href="#4-1-9-useLayoutEffect" class="headerlink" title="4.1.9 useLayoutEffect"></a>4.1.9 useLayoutEffect</h3><p><strong>useLayoutEffect</strong>： 与<code>useEffect</code>基本一致，不同的地方时，<code>useLayoutEffect</code>是<code>同步</code></p>
<p>要注意的是<code>useLayoutEffect</code>在 DOM 更新之后，浏览器绘制之前，这样做的好处是可以更加方便的修改 <code>DOM</code>，获取 <code>DOM</code> 信息,这样浏览器只会绘制一次，所以<code>useLayoutEffect</code>在<code>useEffect</code>之前执行</p>
<p>如果是<code>useEffect</code>的话 ，<code>useEffect</code> 执行在浏览器绘制视图之后，如果在此时改变<code>DOM</code>，有可能会导致浏览器再次<code>回流</code>和<code>重绘</code>。</p>
<p>除此之外<code>useLayoutEffect</code>的 <code>callback</code> 中代码执行会阻塞浏览器绘制</p>
<p>举个例子🌰：</p>
<pre><code class="hljs">import React, &#123; useState, useLayoutEffect, useEffect, useRef &#125; from &#39;react&#39;;
import &#123; Button &#125; from &#39;antd-mobile&#39;;

const Index = () =&gt; &#123;
  const [count, setCount] = useState(0)
  const time = useRef(null)
  
  useEffect(()=&gt;&#123;
    if(time.current)&#123;
      console.log(&quot;useEffect:&quot;, performance.now() - time.current)
    &#125;
  &#125;)

  useLayoutEffect(()=&gt;&#123;
    if(time.current)&#123;
      console.log(&quot;useLayoutEffect:&quot;, performance.now() - time.current)
    &#125;
  &#125;)

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;div&gt;count: &#123;count&#125;&lt;/div&gt;
    &lt;Button
      color=&#39;primary&#39;
      onClick=&#123;() =&gt; &#123;
        setCount(v =&gt; v + 1)
        time.current = performance.now()
      &#125;&#125;  
    &gt;
      加1
    &lt;/Button&gt;
  &lt;/div&gt;
&#125;
 
export default Index
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3c5ed88f799b44b197e4d8d2dd1a0456~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img6.gif"></p>
<h3 id="4-1-10-useDebugValue"><a href="#4-1-10-useDebugValue" class="headerlink" title="4.1.10 useDebugValue"></a>4.1.10 useDebugValue</h3><p><strong>useDebugValue</strong>：可用于在 <code>React</code> 开发者工具中显示<code>自定义 hook 的标签</code></p>
<p>官方并不推荐你向每个自定义 Hook 添加 debug 值。当它作为共享库的一部分时才最有价值。</p>
<pre><code class="hljs">function useFriendStatus(friendID) &#123;
  const [isOnline, setIsOnline] = useState(null);

  // ...

  // 在开发者工具中的这个 Hook 旁边显示标签  
  // e.g. &quot;FriendStatus: Online&quot;  useDebugValue(isOnline ? &#39;Online&#39; : &#39;Offline&#39;);
  return isOnline;
&#125;
复制代码
</code></pre>
<h2 id="4-2-React-v18中的hooks"><a href="#4-2-React-v18中的hooks" class="headerlink" title="4.2 React v18中的hooks"></a>4.2 React v18中的hooks</h2><h3 id="4-2-1-useSyncExternalStore"><a href="#4-2-1-useSyncExternalStore" class="headerlink" title="4.2.1 useSyncExternalStore"></a>4.2.1 useSyncExternalStore</h3><p><strong>useSyncExternalStore</strong>:是一个推荐用于<code>读取</code>和<code>订阅外部数据源</code>的 <code>hook</code>，其方式与选择性的 <code>hydration</code> 和时间切片等并发渲染功能兼容</p>
<p>结构：</p>
<pre><code class="hljs">const state = useSyncExternalStore(subscribe, getSnapshot[, getServerSnapshot])
复制代码
</code></pre>
<ul>
<li>  <code>subscribe</code>: 订阅函数，用于注册一个回调函数，当存储值发生更改时被调用。此外， <code>useSyncExternalStore</code> 会通过带有记忆性的 <code>getSnapshot</code> 来判别数据是否发生变化，如果发生变化，那么会<code>强制更新</code>数据。</li>
<li>  <code>getSnapshot</code>: 返回当前存储值的函数。必须返回缓存的值。如果 <code>getSnapshot</code> 连续多次调用，则必须返回相同的确切值，除非中间有存储值更新。</li>
<li>  <code>getServerSnapshot</code>：返回服务端(<code>hydration</code>模式下)渲染期间使用的存储值的函数</li>
</ul>
<p>举个栗子🌰：</p>
<pre><code class="hljs">import React, &#123;useSyncExternalStore&#125; from &#39;react&#39;;
import &#123; combineReducers , createStore  &#125; from &#39;redux&#39;

const reducer = (state=1,action) =&gt; &#123;
  switch (action.type)&#123;
    case &#39;ADD&#39;:
      return state + 1
    case &#39;DEL&#39;:
      return state - 1
    default:
      return state
  &#125;
&#125;

/* 注册reducer,并创建store */
const rootReducer = combineReducers(&#123; count: reducer  &#125;)
const store = createStore(rootReducer,&#123; count: 1  &#125;)

const Index = () =&gt; &#123;
    // 订阅
    const state = useSyncExternalStore(store.subscribe,() =&gt; store.getState().count)
    return &lt;div&gt;
        &lt;div&gt;&#123;state&#125;&lt;/div&gt;
        &lt;div&gt;
          &lt;button onClick=&#123;() =&gt; store.dispatch(&#123; type:&#39;ADD&#39; &#125;)&#125; &gt;加1&lt;/button&gt;
          &lt;button style=&#123;&#123;marginLeft: 8&#125;&#125; onClick=&#123;() =&gt; store.dispatch(&#123; type:&#39;DEL&#39; &#125;)&#125; &gt;减1&lt;/button&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&#125;

export default Index
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/870ec6a15d1d47ec90604475f9a9c5e0~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img1.gif"></p>
<p>从上述代码可以看出，当点击按钮后，会触发 <code>store.subscribe</code>（订阅函数），执行<code>getSnapshot</code>后得到新的<code>count</code>，如果<code>count</code>发生变化，则会触发更新</p>
<h3 id="4-2-2-useTransition"><a href="#4-2-2-useTransition" class="headerlink" title="4.2.2 useTransition"></a>4.2.2 useTransition</h3><p><strong>useTransition</strong>：返回一个状态值表示过渡任务的等待状态，以及一个启动该过渡任务的函数。</p>
<p>那么什么是过渡任务？</p>
<p>在一些场景中，如：输入框、tab切换、按钮等，这些任务需要视图上<code>立刻</code>做出响应，这些任务可以称之为立即更新的任务</p>
<p>但有的时候，更新任务并不是那么紧急，或者来说要去请求数据等，导致新的状态不能立更新，需要用一个<code>loading...</code>的等待状态，这类任务就是过度任务</p>
<p>结构：</p>
<pre><code class="hljs">const [isPending, startTransition] = useTransition();
复制代码
</code></pre>
<ul>
<li>  <code>isPending</code>：过渡状态的标志，为<code>true</code>时是等待状态</li>
<li>  <code>startTransition</code>：可以将里面的任务变成过渡任务</li>
</ul>
<p>大家可能对上面的描述存在着一些疑问，我们直接举个例子🌰来说明：</p>
<pre><code class="hljs">import React, &#123; useState, useTransition &#125; from &#39;react&#39;;

const Index = () =&gt; &#123;

  const [isPending, startTransition] = useTransition();
  const [input, setInput] = useState(&#39;&#39;);
  const [list, setList] = useState([]);

  return  &lt;div&gt;
      &lt;div&gt;大家好：我是小杜杜~&lt;/div&gt;
      输入框： &lt;input
        value=&#123;input&#125;
        onChange=&#123;(e) =&gt; &#123;
          setInput(e.target.value);
          startTransition(() =&gt; &#123;
            const res = [];
            for (let i = 0; i &lt; 2000; i++) &#123;
              res.push(e.target.value);
            &#125;
            setList(res);
          &#125;);
        &#125;&#125; /&gt;
      &#123;isPending ? (
        &lt;div&gt;加载中...&lt;/div&gt;
      ) : (
        list.map((item, index) =&gt; &lt;div key=&#123;index&#125;&gt;&#123;item&#125;&lt;/div&gt;)
      )&#125;
    &lt;/div&gt;
&#125;

export default Index
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3a1c6c97b8f742dab5d2e76f56e52223~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img3.gif"></p>
<p>实际上，我们在<code>Input</code>输入内容是，会进行增加，假设我们在<code>startTransition</code>中请求一个接口，在接口请求的时候，<code>isPending</code>会为<code>true</code>，就会有一个<code>loading</code>的状态，请求完之后，<code>isPending</code>变为<code>false</code>渲染列表</p>
<h3 id="4-2-3-useDeferredValue"><a href="#4-2-3-useDeferredValue" class="headerlink" title="4.2.3 useDeferredValue"></a>4.2.3 useDeferredValue</h3><p><strong>useDeferredValue</strong>：接受一个值，并返回该值的新副本，该副本将推迟到更紧急地更新之后。</p>
<p>如果当前渲染是一个紧急更新的结果，比如用户输入，<code>React</code> 将返回之前的值，然后在紧急渲染完成后渲染新的值。</p>
<p>也就是说<code>useDeferredValue</code>可以让状态<code>滞后派生</code></p>
<p>结构：</p>
<pre><code class="hljs">const deferredValue = useDeferredValue(value);
复制代码
</code></pre>
<ul>
<li>  <code>value</code>：可变的值，如<code>useState</code>创建的值</li>
<li>  <code>deferredValue</code>: 延时状态</li>
</ul>
<p>这个感觉和<code>useTransition</code>有点相似，还是以输入框的模式，举个栗子🌰：</p>
<pre><code class="hljs">import React, &#123; useState, useDeferredValue &#125; from &#39;react&#39;;

const getList = (key) =&gt; &#123;
  const arr = [];
  for (let i = 0; i &lt; 10000; i++) &#123;
    if (String(i).includes(key)) &#123;
      arr.push(&lt;li key=&#123;i&#125;&gt;&#123;i&#125;&lt;/li&gt;);
    &#125;
  &#125;
  return arr;
&#125;;
const Index = () =&gt; &#123;
  const [value, setValue] = useState(&quot;&quot;);
  const deferredValue = useDeferredValue(value);
  console.log(&#39;value：&#39;, value);
  console.log(&#39;deferredValue：&#39;,deferredValue);

  return (
    &lt;div &gt;
      &lt;div&gt;
        &lt;div&gt;大家好，我是小杜杜&lt;/div&gt;
        输入框：&lt;input onChange=&#123;(e) =&gt; setValue(e.target.value)&#125; /&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;ul&gt;&#123;deferredValue ? getList(deferredValue) : null&#125;&lt;/ul&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
&#125;

export default Index
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7fba0464083c44bf985446b79c2dd55a~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img4.gif"></p>
<h4 id="和useTransition做对比"><a href="#和useTransition做对比" class="headerlink" title="和useTransition做对比"></a>和useTransition做对比</h4><p>根据上面两个示例我们看看<code>useTransition</code>和<code>useDeferredValue</code>做个对比看看</p>
<ul>
<li>  相同点：<code>useDeferredValue</code>和<code>useTransition</code>一样，都是过渡更新任务</li>
<li>  不同点：<code>useTransition</code>给的是一个状态，而<code>useDeferredValue</code>给的是一个值</li>
</ul>
<h3 id="4-2-4-useInsertionEffect"><a href="#4-2-4-useInsertionEffect" class="headerlink" title="4.2.4 useInsertionEffect"></a>4.2.4 useInsertionEffect</h3><p><strong>useInsertionEffect</strong>：与 <code>useEffect</code>一样，但它在所有 DOM 突变 之前同步触发。</p>
<p>我们来看看<code>useInsertionEffect</code>对比于<code>useEffect</code>和<code>useLayoutEffect</code>在执行顺序上有什么区别，栗子🌰：</p>
<pre><code class="hljs">  useEffect(()=&gt;&#123;
    console.log(&#39;useEffect&#39;)
  &#125;,[])

  useLayoutEffect(()=&gt;&#123;
    console.log(&#39;useLayoutEffect&#39;)
  &#125;,[])

  useInsertionEffect(()=&gt;&#123;
    console.log(&#39;useInsertionEffect&#39;)
  &#125;,[])
复制代码
</code></pre>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a9e3d0646cd34de89a5681f60ee94912~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>可以看到在执行顺序上 <code>useInsertionEffect</code> &gt; <code>useLayoutEffect</code> &gt; <code>useEffect</code></p>
<p>特别注意一点：<code>seInsertionEffect</code> 应仅限于 <strong>css-in-js</strong> 库作者使用。优先考虑使用 <code>useEffect</code> 或 <code>useLayoutEffect</code> 来替代。</p>
<p>模拟一下<code>seInsertionEffect</code>的使用🌰：</p>
<pre><code class="hljs">import React, &#123; useInsertionEffect &#125; from &#39;react&#39;;

const Index = () =&gt; &#123;

  useInsertionEffect(()=&gt;&#123;
    const style = document.createElement(&#39;style&#39;)
    style.innerHTML = `
      .css-in-js&#123;
        color: blue;
      &#125;
    `
    document.head.appendChild(style)
 &#125;,[])

  return (
    &lt;div&gt;
        &lt;div className=&#39;css-in-js&#39;&gt;大家好，我是小杜杜&lt;/div&gt;
    &lt;/div&gt;
  );
&#125;

export default Index
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/60517f354457436a8e24dccf0ed5a6a2~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<h3 id="4-2-5-useId"><a href="#4-2-5-useId" class="headerlink" title="4.2.5 useId"></a>4.2.5 useId</h3><p><strong>useId</strong> ： 是一个用于生成横跨服务端和客户端的稳定的<code>唯一 ID</code> 的同时避免<code>hydration</code> 不匹配的 <code>hook</code>。</p>
<p>这里牵扯到<code>SSR</code>的问题，我打算之后在单独写一章，来详细讲讲，所以在这里就介绍一下使用即可</p>
<pre><code class="hljs">    const id = useId();
复制代码
</code></pre>
<p>例子🌰：</p>
<pre><code class="hljs">import React, &#123; useId &#125; from &#39;react&#39;;

const Index = () =&gt; &#123;

  const id = useId()

  return (
    &lt;div&gt;
        &lt;div id=&#123;id&#125; &gt;
          大家好，我是小杜杜
        &lt;/div&gt;
    &lt;/div&gt;
  );
&#125;

export default Index
复制代码
</code></pre>
<p>效果： <img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bff8dd2e73d447e4939f41072772ed85~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<h2 id="4-3-自定义hooks"><a href="#4-3-自定义hooks" class="headerlink" title="4.3 自定义hooks"></a>4.3 自定义hooks</h2><p><code>自定义hooks</code>是在<code>react-hooks</code>基础上的一个扩展，可以根据业务、需求去制定相应的<code>hooks</code>,将常用的逻辑进行封装，从而具备复用性</p>
<p>关于自定义<code>hooks</code>的内容可以看看我之前的文章：<a target="_blank" rel="noopener" href="https://juejin.cn/post/7101486767336849421" title="https://juejin.cn/post/7101486767336849421">搞懂这12个Hooks，保证让你玩转React</a></p>
<p>里面通过分析<code>ahooks</code>源码，讲解了很多不错的自定义<code>hooks</code>，如：<code>useCreation</code>、<code>useReactive</code>、<code>useEventListener</code>等的实现，相信一定能够帮助到各位，感兴趣的可以支持下～</p>
<h1 id="5-react-dom"><a href="#5-react-dom" class="headerlink" title="5 react-dom"></a>5 react-dom</h1><p><strong>react-dom</strong>：这个包提供了用户DOM的特定方法。这个包在<code>React v18</code>中还是做了很大的改动，接下来我们逐个看看</p>
<h2 id="5-1-createPortal"><a href="#5-1-createPortal" class="headerlink" title="5.1 createPortal"></a>5.1 createPortal</h2><p><strong>createPortal</strong>：在<code>Portal</code>中提供了一种将子节点渲染到已 DOM 节点中的方式，该节点存在于 DOM 组件的层次结构之外。</p>
<p>也就是说<code>createPortal</code>可以把当前组件或<code>element</code>元素的子节点，渲染到组件之外的其他地方。</p>
<p>来看看<code>createPortal(child, container)</code>的入参：</p>
<ul>
<li>  <code>child</code>：任何可渲染的子元素</li>
<li>  <code>container</code>：是一个<code>DOM</code>元素</li>
</ul>
<p>看着概念可能并不是很好理解，我们来举个栗子🌰：</p>
<pre><code class="hljs">import React, &#123; useState, useEffect, useRef &#125; from &#39;react&#39;;
import ReactDom from &#39;react-dom&#39;


const Child = (&#123;children&#125;) =&gt; &#123;

  const ref = useRef()
  const [newDom, setNewDom] = useState()

  useEffect(() =&gt; &#123;
    setNewDom(ReactDom.createPortal(children, ref.current))
  &#125;, [])

  return &lt;div&gt;
    &lt;div ref=&#123;ref&#125;&gt;同级的节点&lt;/div&gt;
    &lt;div&gt;
      这层的节点
      &#123;newDom&#125;
    &lt;/div&gt;
  &lt;/div&gt;
&#125;

const Index = () =&gt; &#123;

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;Child&gt;
      &lt;div&gt;大家好，我是小杜杜&lt;/div&gt;
    &lt;/Child&gt;
  &lt;/div&gt;
&#125;

export default Index;
复制代码
</code></pre>
<p>要注意下<code>Child</code>:</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2f8d35a22bfb4d619294efba52923b91~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"> 我们传入的<code>children</code>被<code>createPortal</code>包裹后，<code>children</code>的节点位置会如何？</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f3e92824e6d5438bbc1e9b438224fddf~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"> 发现，我们处理的数<code>newDom</code>的数据到了同级的节点处，那么这个<code>Api</code>该如何应用呢？</p>
<p>我们可以处理一些顶层元素，如：<code>Modal</code>弹框组件，<code>Modal</code>组件在内部中书写，挂载到外层的容器（如body），此时这个<code>Api</code>就非常有用</p>
<h2 id="5-2-flushSync"><a href="#5-2-flushSync" class="headerlink" title="5.2 flushSync"></a>5.2 flushSync</h2><p><strong>flushSync</strong>：可以将回调函数中的更新任务，放到一个较高级的优先级中，适用于强制刷新，同时确保了<code>DOM</code>会被立即更新</p>
<p>为了更好的理解，我们举个栗子🌰：</p>
<pre><code class="hljs">import &#123; Button &#125; from &#39;antd-mobile&#39;;
import React, &#123; Component&#125; from &#39;react&#39;;
import ReactDOM from &#39;react-dom&#39;
 
class Index extends Component&#123;

  constructor(props)&#123;
    super(props)
    this.state=&#123;
      number: 0
    &#125;
  &#125;

  render()&#123;
    const &#123; number &#125; = this.state
    console.log(number)
    return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
      &lt;div&gt;数字: &#123;number&#125;&lt;/div&gt; 
      &lt;Button
        color=&#39;primary&#39;
        onClick=&#123;() =&gt; &#123;
          this.setState(&#123; number: 1  &#125;)
          this.setState(&#123; number: 2  &#125;)
          this.setState(&#123; number: 3  &#125;)
        &#125;&#125;
      &gt;
        点击 
      &lt;/Button&gt;    
    &lt;/div&gt;
  &#125;
&#125;

export default Index;
复制代码
</code></pre>
<p>我们看看点击按钮会打印出什么？</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0c3803007e4e425c8af37620a385d8db~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img6.gif"></p>
<p>这个不难理解，因为<code>this.setState</code>会进行批量更新，所以打印出的是3 接下来，我们用<code>flushSync</code>处理下<code>number: 2</code> 来看看是什么效果：</p>
<pre><code class="hljs">    onClick=&#123;() =&gt; &#123;
      this.setState(&#123; number: 1  &#125;)
      ReactDOM.flushSync(()=&gt;&#123;
        this.setState(&#123; number: 2  &#125;)
      &#125;)
      this.setState(&#123; number: 3  &#125;)
    &#125;&#125;
复制代码
</code></pre>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a67cde72ec864d888d01b6fe96277076~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img1.gif"></p>
<p>可以发现<code>flushSync</code>会优先执行，并且强制刷新，所以会改变<code>number</code>值为2，然后<code>1</code>和<code>3</code>在被批量刷新，更新为3</p>
<h2 id="5-3-render"><a href="#5-3-render" class="headerlink" title="5.3 render"></a>5.3 render</h2><p><strong>render</strong>：这个是我们在<code>react-dom</code>中最常用的Api，用于渲染一个<code>react</code>元素</p>
<p>我们通常使用在根部，如：</p>
<pre><code class="hljs">ReactDOM.render( 
    &lt; App / &gt;, 
    document.getElementById(&#39;app&#39;)
)
复制代码
</code></pre>
<h3 id="5-3-1-createRoot"><a href="#5-3-1-createRoot" class="headerlink" title="5.3.1 createRoot"></a>5.3.1 createRoot</h3><p>在<code>React v18</code>中，<code>render</code>函数已经被<code>createRoot</code>所替代</p>
<p><code>createRoot</code>会控制你传入的容器节点的内容。当调用 render 时，里面的任何现有 DOM 元素都会被替换。后面的调用使用 React 的 DOM diffing 算法进行有效更新。</p>
<p>并且<code>createRoot</code>不修改容器节点（只修改容器的子节点）。可以在不覆盖现有子节点的情况下将组件插入现有 DOM 节点。</p>
<p>如：</p>
<pre><code class="hljs">import React, &#123; StrictMode &#125; from &#39;react&#39;;
import &#123; createRoot &#125; from &#39;react-dom/client&#39;;

const rootElement = document.getElementById(&#39;root&#39;);
const root = createRoot(rootElement);

root.render(
  &lt;StrictMode&gt;
    &lt;Main /&gt;
  &lt;/StrictMode&gt;
);
复制代码
</code></pre>
<h2 id="5-4-hydrate"><a href="#5-4-hydrate" class="headerlink" title="5.4 hydrate"></a>5.4 hydrate</h2><p><code>hydrate</code>：服务端渲染用<code>hydrate</code>与 <code>render()</code>相同，但它用于在 <code>ReactDOMServer</code> 渲染的容器中对 HTML 的内容进行 hydrate 操作。</p>
<pre><code class="hljs">hydrate(element, container[, callback])
复制代码
</code></pre>
<h3 id="5-4-1-hydrateRoot"><a href="#5-4-1-hydrateRoot" class="headerlink" title="5.4.1 hydrateRoot()"></a>5.4.1 hydrateRoot()</h3><p><code>hydrate</code>在<code>React v18</code>也被替代为<code>hydrateRoot()</code></p>
<pre><code class="hljs">hydrateRoot(container, element[, options])
复制代码
</code></pre>
<h2 id="5-5-unmountComponentAtNode"><a href="#5-5-unmountComponentAtNode" class="headerlink" title="5.5 unmountComponentAtNode"></a>5.5 unmountComponentAtNode</h2><p><strong>unmountComponentAtNode</strong>：从 DOM 中卸载组件，会将其事件处理器（event handlers）和 state 一并清除。如果指定容器上没有对应已挂载的组件，这个函数什么也不会做。如果组件被移除将会返回 <code>true</code>，如果没有组件可被移除将会返回 <code>false</code>。</p>
<p>举个栗子🌰：</p>
<pre><code class="hljs">import &#123; Button &#125; from &#39;antd-mobile&#39;;
import React, &#123; Component&#125; from &#39;react&#39;;
import ReactDOM from &#39;react-dom&#39;
 
const Child = () =&gt; &#123;
  return &lt;div&gt;大家好，我是小杜杜&lt;/div&gt;
&#125;

class Index extends Component&#123;

  constructor(props)&#123;
    super(props)
    this.state=&#123;
      number: 0
    &#125;
  &#125;

  node = null

  componentDidMount()&#123;
    ReactDOM.render(&lt;Child/&gt;, this.node) // 创建一个容器
  &#125;

  render()&#123;
    const &#123; number &#125; = this.state
    console.log(number)
    return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
      &lt;div ref=&#123;(node) =&gt; this.node = node&#125;&gt;&lt;/div&gt; 
      &lt;Button
        color=&#39;primary&#39;
        onClick=&#123;() =&gt; &#123;
          const res = ReactDOM.unmountComponentAtNode(this.node)
          console.log(res)
        &#125;&#125;
      &gt;
        卸载 
      &lt;/Button&gt;    
    &lt;/div&gt;
  &#125;
&#125;

export default Index;
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0bf9d42f26414d759defe4c28d4a4fb4~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img2.gif"></p>
<h3 id="5-5-1-root-unmount"><a href="#5-5-1-root-unmount" class="headerlink" title="5.5.1 root.unmount()"></a>5.5.1 root.unmount()</h3><p><code>unmountComponentAtNode</code> 同样在<code>React 18</code>中被替代了，替换成了<code>createRoot</code>中的<code>unmount()</code>方法</p>
<pre><code class="hljs">const root = createRoot(container);
root.render(element);

root.unmount()
复制代码
</code></pre>
<h2 id="5-6-findDOMNode"><a href="#5-6-findDOMNode" class="headerlink" title="5.6 findDOMNode"></a>5.6 findDOMNode</h2><p><strong>findDOMNode</strong>：用于访问组件<code>DOM</code>元素节点（应急方案），官方推荐使用<code>ref</code></p>
<p>需要注意的是：</p>
<ul>
<li><p>  <code>findDOMNode</code>只能用到<code>挂载</code>的组件上</p>
</li>
<li><p>  <code>findDOMNode</code>只能用于类组件，不能用于函数式组件</p>
</li>
<li><p>  如果组件渲染为<code>null</code>或者为<code>false</code>，那么<code>findDOMNode</code>返回的值也是<code>null</code></p>
</li>
<li><p>  如果是多个子节点<code>Fragment</code>的情况，<code>findDOMNode</code>会返回第一个非空子节点对应的 DOM 节点。</p>
</li>
<li><p>在严格模式下这个方法已经被<code>弃用</code> 举个例子🌰：</p>
<p>  import { Button } from ‘antd-mobile’;<br>  import React, { Component} from ‘react’;<br>  import ReactDOM from ‘react-dom’</p>
<p>  class Index extends Component{</p>
<pre><code class="hljs">render()&#123;

  return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
    &lt;div&gt;大家好，我是小杜杜&lt;/div&gt; 
    &lt;Button
      color=&#39;primary&#39;
      onClick=&#123;() =&gt; &#123;
        console.log(ReactDOM.findDOMNode(this))
      &#125;&#125;
    &gt;
      获取容器
    &lt;/Button&gt;    
  &lt;/div&gt;
&#125;
</code></pre>
<p>  }</p>
<p>  export default Index;<br>  复制代码</p>
</li>
</ul>
<p>效果： <img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d870212ce4a94f43ba040f384564d74d~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img3.gif"></p>
<h2 id="5-7-unstable-batchedUpdates"><a href="#5-7-unstable-batchedUpdates" class="headerlink" title="5.7 unstable_batchedUpdates"></a>5.7 unstable_batchedUpdates</h2><p><strong>unstable_batchedUpdates</strong> :可用于手动批量更新state，可以指定多个<code>setState</code>合并为一个更新请求</p>
<p>那么这块手动合并，用在什么情况下呢？来看看下面的场景：</p>
<pre><code class="hljs">import &#123; Button &#125; from &#39;antd-mobile&#39;;
import React, &#123; Component&#125; from &#39;react&#39;;
import ReactDOM from &#39;react-dom&#39;
 
class Index extends Component&#123;

  constructor(props)&#123;
    super(props)
    this.state=&#123;
      number: 0
    &#125;
  &#125;

  render()&#123;
    const &#123; number &#125; = this.state
    return &lt;div style=&#123;&#123;padding: 20&#125;&#125;&gt;
      &lt;div&gt;数字: &#123;number&#125;&lt;/div&gt; 
      &lt;Button
        color=&#39;primary&#39;
        onClick=&#123;() =&gt; &#123;
          this.setState(&#123; number: this.state.number + 1 &#125;)
          console.log(this.state.number)
          this.setState(&#123; number: this.state.number + 1  &#125;)
          console.log(this.state.number)
          this.setState(&#123; number: this.state.number + 1 &#125;)
          console.log(this.state.number)
        &#125;&#125;
      &gt;
        点击 
      &lt;/Button&gt;    
    &lt;/div&gt;
  &#125;
&#125;

export default Index
复制代码
</code></pre>
<p>当我们点击按钮后，三个打印会打印出什么？</p>
<p><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/965cbd1c7ee8476db0e62b8e27e34d7a~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"> 此时的场景只会执行一次，并且渲染一次，渲染时为1</p>
<p>那么我们打破<code>React</code>的机制，比如说使用<code>setTimeout</code>绕过，再来看看会打印出什么：</p>
<pre><code class="hljs">      &lt;Button
        color=&#39;primary&#39;
        onClick=&#123;() =&gt; &#123;
          setTimeout(() =&gt; &#123;
            this.setState(&#123; number: this.state.number + 1 &#125;)
            console.log(this.state.number)
            this.setState(&#123; number: this.state.number + 1  &#125;)
            console.log(this.state.number)
            this.setState(&#123; number: this.state.number + 1 &#125;)
            console.log(this.state.number)
          &#125;, 100)
        &#125;&#125;
      &gt;
        点击 
      &lt;/Button&gt;  
复制代码
</code></pre>
<p>此时就会这样：</p>
<p><img src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/20a4768c591d4342a0275bc3a5f66768~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="image.png"></p>
<p>因为绕过了事件机制，此时就会渲染3次，并且渲染的结果为3</p>
<p>那么我们现在想在<code>setTimeout</code>实现<code>React</code>的事件机制该怎么办？就需要用到<code>unstable_batchedUpdates</code>来解决这类问题</p>
<pre><code class="hljs">      &lt;Button
        color=&#39;primary&#39;
        onClick=&#123;() =&gt; &#123;
          setTimeout(() =&gt; &#123;
            ReactDOM.unstable_batchedUpdates(() =&gt; &#123;
              this.setState(&#123; number: this.state.number + 1 &#125;)
              console.log(this.state.number)
              this.setState(&#123; number: this.state.number + 1  &#125;)
              console.log(this.state.number)
              this.setState(&#123; number: this.state.number + 1 &#125;)
              console.log(this.state.number)
            &#125;)
          &#125;, 100)
        &#125;&#125;
      &gt;
        点击 
      &lt;/Button&gt; 
复制代码
</code></pre>
<p>效果：</p>
<p><img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/09a2ffb14bd5466d871292f271ca0eab~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp" srcset="https://ikkiss.oss-cn-hangzhou.aliyuncs.com/images/myblog/loading.gif" lazyload alt="img4.gif"></p>
<h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><h2 id="参考文档"><a href="#参考文档" class="headerlink" title="参考文档"></a>参考文档</h2><ul>
<li>  <a href="https://link.juejin.cn/?target=https://reactjs.org/docs/react-api.html" title="https://reactjs.org/docs/react-api.html">react 官方文档</a></li>
<li>  <a target="_blank" rel="noopener" href="https://juejin.cn/post/7118937685653192735" title="https://juejin.cn/post/7118937685653192735">「React 进阶」 React 全部 Hooks 使用大全 （包含 React v18 版本 ）</a></li>
</ul>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文基本总结了<code>React</code>的所有<code>Api</code>，如果有没写到的，或者是<code>Api</code>用法没写全的，请在下方评论区留言，尽量把这篇文章打造成最全的～</p>
<p>主要包扩<code>组件类</code>、<code>工具类</code>、<code>生命周期</code>、<code>react-hooks</code>、<code>react-dom</code>五大模块的内容，如果你能耐心的看完，相信你对<code>React</code>一定有了更深的理解，同时建议初学者亲自尝试一遍，看看这些<code>Api</code>怎么用，如何用～</p>
<p>至此，签约计划的三篇文章就写完了，说实话，太累了，写出硬文确实比较难,还是希望大家多多关注这个专栏，后续将会带来更好，更全，更容易理解的<code>React</code>文章，还请各位小伙伴多多支持，<code>点赞</code> + <code>收藏</code> 哦～</p>
<p>其他<code>react</code>好文：</p>
<ul>
<li>  <a target="_blank" rel="noopener" href="https://juejin.cn/editor/drafts/7101481981212524552" title="https://juejin.cn/editor/drafts/7101481981212524552">搞懂这12个Hooks，保证让你玩转React</a></li>
<li>  <a target="_blank" rel="noopener" href="https://juejin.cn/post/7103345085089054727" title="https://juejin.cn/post/7103345085089054727">作为一名React，我是这样理解HOC的！</a></li>
<li>  <a target="_blank" rel="noopener" href="https://juejin.cn/post/7116326409961734152" title="https://juejin.cn/post/7116326409961734152">「React深入」一文吃透虚拟DOM和diff算法</a></li>
<li>  <a target="_blank" rel="noopener" href="https://juejin.cn/post/7121551701731409934/" title="https://juejin.cn/post/7121551701731409934/">花三个小时，完全掌握分片渲染和虚拟列表～</a></li>
</ul>
<p>我正在参与掘金技术社区创作者签约计划招募活动，<a target="_blank" rel="noopener" href="https://juejin.cn/post/7112770927082864653" title="https://juejin.cn/post/7112770927082864653">点击链接报名投稿</a>。</p>
<p>.markdown-body pre,.markdown-body pre&gt;code.hljs{color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:700}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}</p>

                
              </div>
            
            <hr/>
            <div>
              <div class="post-metas my-3">
  
    <div class="post-meta mr-3 d-flex align-items-center">
      <i class="iconfont icon-category"></i>
      

<span class="category-chains">
  
  
    
      <span class="category-chain">
        
  <a href="/categories/react/" class="category-chain-item">react</a>
  
  
    <span>></span>
    
  <a href="/categories/react/%E5%9F%BA%E7%A1%80/" class="category-chain-item">基础</a>
  
  

  

      </span>
    
  
</span>

    </div>
  
  
    <div class="post-meta">
      <i class="iconfont icon-tags"></i>
      
        <a href="/tags/react/">#react</a>
      
    </div>
  
</div>


              
  

  <div class="license-box my-3">
    <div class="license-title">
      <div>react-api</div>
      <div>http://81.68.132.127/2022/10/17/react/basic/react-all-api/</div>
    </div>
    <div class="license-meta">
      
        <div class="license-meta-item">
          <div>作者</div>
          <div>Ikki</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>发布于</div>
          <div>2022年10月17日</div>
        </div>
      
      
        <div class="license-meta-item license-meta-date">
          <div>更新于</div>
          <div>2022年10月30日</div>
        </div>
      
      
        <div class="license-meta-item">
          <div>许可协议</div>
          <div>
            
              
              
                <a target="_blank" href="https://creativecommons.org/licenses/by/4.0/">
                  <span class="hint--top hint--rounded" aria-label="BY - 署名">
                    <i class="iconfont icon-by"></i>
                  </span>
                </a>
              
            
          </div>
        </div>
      
    </div>
    <div class="license-icon iconfont"></div>
  </div>



              
                <div class="post-prevnext my-3">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/2022/10/20/JS/binary/%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%9F%BA%E7%A1%80/" title="进制转换">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">进制转换</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2022/10/07/network/http%E5%8D%8F%E8%AE%AE/" title="HTTP协议">
                        <span class="hidden-mobile">HTTP协议</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
  <article id="comments" lazyload>
    
  <div id="valine"></div>
  <script type="text/javascript">
    Fluid.utils.loadComments('#valine', function() {
      Fluid.utils.createScript('https://lib.baomitu.com/valine/1.5.1/Valine.min.js', function() {
        var options = Object.assign(
          {"appId":"6KzYWc2oaEBaHgNl4UIicMJC-gzGzoHsz","appKey":"vJ4FIwKeHRmHraCJraAsjyZ7","path":"window.location.pathname","placeholder":"请输入你的评论\n昵称为必填项目(输入QQ号码可以自动获取昵称和邮箱)\n虽然email不是必选，但是填写了email可以收到推送通知哦！","avatar":"retro","meta":["nick","mail","link"],"requiredFields":[],"pageSize":10,"lang":"zh-CN","highlight":true,"recordIP":true,"serverURLs":"","emojiCDN":null,"emojiMaps":null,"enableQQ":true},
          {
            el: "#valine",
            path: window.location.pathname
          }
        )
        new Valine(options);
        Fluid.utils.waitElementVisible('#valine .vcontent', () => {
          var imgSelector = '#valine .vcontent img:not(.vemoji)';
          Fluid.plugins.imageCaption(imgSelector);
          Fluid.plugins.fancyBox(imgSelector);
        })
      });
    });
  </script>
  <noscript>Please enable JavaScript to view the comments</noscript>


  </article>


          </article>
        </div>
      </div>
    </div>

    <div class="side-col d-none d-lg-block col-lg-2">
      

    </div>
  </div>
</div>





  



  



  



  



  







    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v" for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>

    

    
  </main>

  <footer>
    <div class="footer-inner">
  
    <div class="footer-content">
      IKKI-专业BUG开发
    </div>
  
  
    <div class="statistics">
  
  

  
    
      <span id="busuanzi_container_site_pv" style="display: none">
        总访问量 
        <span id="busuanzi_value_site_pv"></span>
         次
      </span>
    
    
      <span id="busuanzi_container_site_uv" style="display: none">
        总访客数 
        <span id="busuanzi_value_site_uv"></span>
         人
      </span>
    
    
  
</div>

  
  
  
</div>

  </footer>

  <!-- Scripts -->
  
  <script  src="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://lib.baomitu.com/nprogress/0.2.0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://lib.baomitu.com/jquery/3.6.0/jquery.min.js" ></script>
<script  src="https://lib.baomitu.com/twitter-bootstrap/4.6.1/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>


  <script  src="https://lib.baomitu.com/typed.js/2.0.12/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var subtitle = document.getElementById('subtitle');
      if (!subtitle || !typing) {
        return;
      }
      var text = subtitle.getAttribute('data-typed-text');
      
        typing(text);
      
    })(window, document);
  </script>




  
    <script  src="/js/img-lazyload.js" ></script>
  




  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/tocbot/4.18.2/tocbot.min.js', function() {
    var toc = jQuery('#toc');
    if (toc.length === 0 || !window.tocbot) { return; }
    var boardCtn = jQuery('#board-ctn');
    var boardTop = boardCtn.offset().top;

    window.tocbot.init(Object.assign({
      tocSelector     : '#toc-body',
      contentSelector : '.markdown-body',
      linkClass       : 'tocbot-link',
      activeLinkClass : 'tocbot-active-link',
      listClass       : 'tocbot-list',
      isCollapsedClass: 'tocbot-is-collapsed',
      collapsibleClass: 'tocbot-is-collapsible',
      scrollSmooth    : true,
      includeTitleTags: true,
      headingsOffset  : -boardTop,
    }, CONFIG.toc));
    if (toc.find('.toc-list-item').length > 0) {
      toc.css('visibility', 'visible');
    }

    Fluid.events.registerRefreshCallback(function() {
      if ('tocbot' in window) {
        tocbot.refresh();
        var toc = jQuery('#toc');
        if (toc.length === 0 || !tocbot) {
          return;
        }
        if (toc.find('.toc-list-item').length > 0) {
          toc.css('visibility', 'visible');
        }
      }
    });
  });
</script>


  <script src=https://lib.baomitu.com/clipboard.js/2.0.11/clipboard.min.js></script>

  <script>Fluid.plugins.codeWidget();</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/anchor-js/4.3.1/anchor.min.js', function() {
    window.anchors.options = {
      placement: CONFIG.anchorjs.placement,
      visible  : CONFIG.anchorjs.visible
    };
    if (CONFIG.anchorjs.icon) {
      window.anchors.options.icon = CONFIG.anchorjs.icon;
    }
    var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
    var res = [];
    for (var item of el) {
      res.push('.markdown-body > ' + item.trim());
    }
    if (CONFIG.anchorjs.placement === 'left') {
      window.anchors.options.class = 'anchorjs-link-left';
    }
    window.anchors.add(res.join(', '));

    Fluid.events.registerRefreshCallback(function() {
      if ('anchors' in window) {
        anchors.removeAll();
        var el = (CONFIG.anchorjs.element || 'h1,h2,h3,h4,h5,h6').split(',');
        var res = [];
        for (var item of el) {
          res.push('.markdown-body > ' + item.trim());
        }
        if (CONFIG.anchorjs.placement === 'left') {
          anchors.options.class = 'anchorjs-link-left';
        }
        anchors.add(res.join(', '));
      }
    });
  });
</script>


  
<script>
  Fluid.utils.createScript('https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js', function() {
    Fluid.plugins.fancyBox();
  });
</script>


  <script>Fluid.plugins.imageCaption();</script>

  <script  src="/js/local-search.js" ></script>

  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>




  
<script src="/fluid/js/star.js"></script>



<!-- 主题的启动项，将它保持在最底部 -->
<!-- the boot of the theme, keep it at the bottom -->
<script  src="/js/boot.js" ></script>


  

  <noscript>
    <div class="noscript-warning">博客在允许 JavaScript 运行的环境下浏览效果更佳</div>
  </noscript>
</body>
</html>
